1*1c12ee1eSDan Willemsen// Copyright 2019 The Go Authors. All rights reserved. 2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style 3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file. 4*1c12ee1eSDan Willemsen 5*1c12ee1eSDan Willemsenpackage protojson 6*1c12ee1eSDan Willemsen 7*1c12ee1eSDan Willemsenimport ( 8*1c12ee1eSDan Willemsen "encoding/base64" 9*1c12ee1eSDan Willemsen "fmt" 10*1c12ee1eSDan Willemsen 11*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/encoding/json" 12*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/encoding/messageset" 13*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/errors" 14*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/filedesc" 15*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/flags" 16*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/genid" 17*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/order" 18*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/pragma" 19*1c12ee1eSDan Willemsen "google.golang.org/protobuf/proto" 20*1c12ee1eSDan Willemsen "google.golang.org/protobuf/reflect/protoreflect" 21*1c12ee1eSDan Willemsen "google.golang.org/protobuf/reflect/protoregistry" 22*1c12ee1eSDan Willemsen) 23*1c12ee1eSDan Willemsen 24*1c12ee1eSDan Willemsenconst defaultIndent = " " 25*1c12ee1eSDan Willemsen 26*1c12ee1eSDan Willemsen// Format formats the message as a multiline string. 27*1c12ee1eSDan Willemsen// This function is only intended for human consumption and ignores errors. 28*1c12ee1eSDan Willemsen// Do not depend on the output being stable. It may change over time across 29*1c12ee1eSDan Willemsen// different versions of the program. 30*1c12ee1eSDan Willemsenfunc Format(m proto.Message) string { 31*1c12ee1eSDan Willemsen return MarshalOptions{Multiline: true}.Format(m) 32*1c12ee1eSDan Willemsen} 33*1c12ee1eSDan Willemsen 34*1c12ee1eSDan Willemsen// Marshal writes the given proto.Message in JSON format using default options. 35*1c12ee1eSDan Willemsen// Do not depend on the output being stable. It may change over time across 36*1c12ee1eSDan Willemsen// different versions of the program. 37*1c12ee1eSDan Willemsenfunc Marshal(m proto.Message) ([]byte, error) { 38*1c12ee1eSDan Willemsen return MarshalOptions{}.Marshal(m) 39*1c12ee1eSDan Willemsen} 40*1c12ee1eSDan Willemsen 41*1c12ee1eSDan Willemsen// MarshalOptions is a configurable JSON format marshaler. 42*1c12ee1eSDan Willemsentype MarshalOptions struct { 43*1c12ee1eSDan Willemsen pragma.NoUnkeyedLiterals 44*1c12ee1eSDan Willemsen 45*1c12ee1eSDan Willemsen // Multiline specifies whether the marshaler should format the output in 46*1c12ee1eSDan Willemsen // indented-form with every textual element on a new line. 47*1c12ee1eSDan Willemsen // If Indent is an empty string, then an arbitrary indent is chosen. 48*1c12ee1eSDan Willemsen Multiline bool 49*1c12ee1eSDan Willemsen 50*1c12ee1eSDan Willemsen // Indent specifies the set of indentation characters to use in a multiline 51*1c12ee1eSDan Willemsen // formatted output such that every entry is preceded by Indent and 52*1c12ee1eSDan Willemsen // terminated by a newline. If non-empty, then Multiline is treated as true. 53*1c12ee1eSDan Willemsen // Indent can only be composed of space or tab characters. 54*1c12ee1eSDan Willemsen Indent string 55*1c12ee1eSDan Willemsen 56*1c12ee1eSDan Willemsen // AllowPartial allows messages that have missing required fields to marshal 57*1c12ee1eSDan Willemsen // without returning an error. If AllowPartial is false (the default), 58*1c12ee1eSDan Willemsen // Marshal will return error if there are any missing required fields. 59*1c12ee1eSDan Willemsen AllowPartial bool 60*1c12ee1eSDan Willemsen 61*1c12ee1eSDan Willemsen // UseProtoNames uses proto field name instead of lowerCamelCase name in JSON 62*1c12ee1eSDan Willemsen // field names. 63*1c12ee1eSDan Willemsen UseProtoNames bool 64*1c12ee1eSDan Willemsen 65*1c12ee1eSDan Willemsen // UseEnumNumbers emits enum values as numbers. 66*1c12ee1eSDan Willemsen UseEnumNumbers bool 67*1c12ee1eSDan Willemsen 68*1c12ee1eSDan Willemsen // EmitUnpopulated specifies whether to emit unpopulated fields. It does not 69*1c12ee1eSDan Willemsen // emit unpopulated oneof fields or unpopulated extension fields. 70*1c12ee1eSDan Willemsen // The JSON value emitted for unpopulated fields are as follows: 71*1c12ee1eSDan Willemsen // ╔═══════╤════════════════════════════╗ 72*1c12ee1eSDan Willemsen // ║ JSON │ Protobuf field ║ 73*1c12ee1eSDan Willemsen // ╠═══════╪════════════════════════════╣ 74*1c12ee1eSDan Willemsen // ║ false │ proto3 boolean fields ║ 75*1c12ee1eSDan Willemsen // ║ 0 │ proto3 numeric fields ║ 76*1c12ee1eSDan Willemsen // ║ "" │ proto3 string/bytes fields ║ 77*1c12ee1eSDan Willemsen // ║ null │ proto2 scalar fields ║ 78*1c12ee1eSDan Willemsen // ║ null │ message fields ║ 79*1c12ee1eSDan Willemsen // ║ [] │ list fields ║ 80*1c12ee1eSDan Willemsen // ║ {} │ map fields ║ 81*1c12ee1eSDan Willemsen // ╚═══════╧════════════════════════════╝ 82*1c12ee1eSDan Willemsen EmitUnpopulated bool 83*1c12ee1eSDan Willemsen 84*1c12ee1eSDan Willemsen // Resolver is used for looking up types when expanding google.protobuf.Any 85*1c12ee1eSDan Willemsen // messages. If nil, this defaults to using protoregistry.GlobalTypes. 86*1c12ee1eSDan Willemsen Resolver interface { 87*1c12ee1eSDan Willemsen protoregistry.ExtensionTypeResolver 88*1c12ee1eSDan Willemsen protoregistry.MessageTypeResolver 89*1c12ee1eSDan Willemsen } 90*1c12ee1eSDan Willemsen} 91*1c12ee1eSDan Willemsen 92*1c12ee1eSDan Willemsen// Format formats the message as a string. 93*1c12ee1eSDan Willemsen// This method is only intended for human consumption and ignores errors. 94*1c12ee1eSDan Willemsen// Do not depend on the output being stable. It may change over time across 95*1c12ee1eSDan Willemsen// different versions of the program. 96*1c12ee1eSDan Willemsenfunc (o MarshalOptions) Format(m proto.Message) string { 97*1c12ee1eSDan Willemsen if m == nil || !m.ProtoReflect().IsValid() { 98*1c12ee1eSDan Willemsen return "<nil>" // invalid syntax, but okay since this is for debugging 99*1c12ee1eSDan Willemsen } 100*1c12ee1eSDan Willemsen o.AllowPartial = true 101*1c12ee1eSDan Willemsen b, _ := o.Marshal(m) 102*1c12ee1eSDan Willemsen return string(b) 103*1c12ee1eSDan Willemsen} 104*1c12ee1eSDan Willemsen 105*1c12ee1eSDan Willemsen// Marshal marshals the given proto.Message in the JSON format using options in 106*1c12ee1eSDan Willemsen// MarshalOptions. Do not depend on the output being stable. It may change over 107*1c12ee1eSDan Willemsen// time across different versions of the program. 108*1c12ee1eSDan Willemsenfunc (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) { 109*1c12ee1eSDan Willemsen return o.marshal(m) 110*1c12ee1eSDan Willemsen} 111*1c12ee1eSDan Willemsen 112*1c12ee1eSDan Willemsen// marshal is a centralized function that all marshal operations go through. 113*1c12ee1eSDan Willemsen// For profiling purposes, avoid changing the name of this function or 114*1c12ee1eSDan Willemsen// introducing other code paths for marshal that do not go through this. 115*1c12ee1eSDan Willemsenfunc (o MarshalOptions) marshal(m proto.Message) ([]byte, error) { 116*1c12ee1eSDan Willemsen if o.Multiline && o.Indent == "" { 117*1c12ee1eSDan Willemsen o.Indent = defaultIndent 118*1c12ee1eSDan Willemsen } 119*1c12ee1eSDan Willemsen if o.Resolver == nil { 120*1c12ee1eSDan Willemsen o.Resolver = protoregistry.GlobalTypes 121*1c12ee1eSDan Willemsen } 122*1c12ee1eSDan Willemsen 123*1c12ee1eSDan Willemsen internalEnc, err := json.NewEncoder(o.Indent) 124*1c12ee1eSDan Willemsen if err != nil { 125*1c12ee1eSDan Willemsen return nil, err 126*1c12ee1eSDan Willemsen } 127*1c12ee1eSDan Willemsen 128*1c12ee1eSDan Willemsen // Treat nil message interface as an empty message, 129*1c12ee1eSDan Willemsen // in which case the output in an empty JSON object. 130*1c12ee1eSDan Willemsen if m == nil { 131*1c12ee1eSDan Willemsen return []byte("{}"), nil 132*1c12ee1eSDan Willemsen } 133*1c12ee1eSDan Willemsen 134*1c12ee1eSDan Willemsen enc := encoder{internalEnc, o} 135*1c12ee1eSDan Willemsen if err := enc.marshalMessage(m.ProtoReflect(), ""); err != nil { 136*1c12ee1eSDan Willemsen return nil, err 137*1c12ee1eSDan Willemsen } 138*1c12ee1eSDan Willemsen if o.AllowPartial { 139*1c12ee1eSDan Willemsen return enc.Bytes(), nil 140*1c12ee1eSDan Willemsen } 141*1c12ee1eSDan Willemsen return enc.Bytes(), proto.CheckInitialized(m) 142*1c12ee1eSDan Willemsen} 143*1c12ee1eSDan Willemsen 144*1c12ee1eSDan Willemsentype encoder struct { 145*1c12ee1eSDan Willemsen *json.Encoder 146*1c12ee1eSDan Willemsen opts MarshalOptions 147*1c12ee1eSDan Willemsen} 148*1c12ee1eSDan Willemsen 149*1c12ee1eSDan Willemsen// typeFieldDesc is a synthetic field descriptor used for the "@type" field. 150*1c12ee1eSDan Willemsenvar typeFieldDesc = func() protoreflect.FieldDescriptor { 151*1c12ee1eSDan Willemsen var fd filedesc.Field 152*1c12ee1eSDan Willemsen fd.L0.FullName = "@type" 153*1c12ee1eSDan Willemsen fd.L0.Index = -1 154*1c12ee1eSDan Willemsen fd.L1.Cardinality = protoreflect.Optional 155*1c12ee1eSDan Willemsen fd.L1.Kind = protoreflect.StringKind 156*1c12ee1eSDan Willemsen return &fd 157*1c12ee1eSDan Willemsen}() 158*1c12ee1eSDan Willemsen 159*1c12ee1eSDan Willemsen// typeURLFieldRanger wraps a protoreflect.Message and modifies its Range method 160*1c12ee1eSDan Willemsen// to additionally iterate over a synthetic field for the type URL. 161*1c12ee1eSDan Willemsentype typeURLFieldRanger struct { 162*1c12ee1eSDan Willemsen order.FieldRanger 163*1c12ee1eSDan Willemsen typeURL string 164*1c12ee1eSDan Willemsen} 165*1c12ee1eSDan Willemsen 166*1c12ee1eSDan Willemsenfunc (m typeURLFieldRanger) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { 167*1c12ee1eSDan Willemsen if !f(typeFieldDesc, protoreflect.ValueOfString(m.typeURL)) { 168*1c12ee1eSDan Willemsen return 169*1c12ee1eSDan Willemsen } 170*1c12ee1eSDan Willemsen m.FieldRanger.Range(f) 171*1c12ee1eSDan Willemsen} 172*1c12ee1eSDan Willemsen 173*1c12ee1eSDan Willemsen// unpopulatedFieldRanger wraps a protoreflect.Message and modifies its Range 174*1c12ee1eSDan Willemsen// method to additionally iterate over unpopulated fields. 175*1c12ee1eSDan Willemsentype unpopulatedFieldRanger struct{ protoreflect.Message } 176*1c12ee1eSDan Willemsen 177*1c12ee1eSDan Willemsenfunc (m unpopulatedFieldRanger) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { 178*1c12ee1eSDan Willemsen fds := m.Descriptor().Fields() 179*1c12ee1eSDan Willemsen for i := 0; i < fds.Len(); i++ { 180*1c12ee1eSDan Willemsen fd := fds.Get(i) 181*1c12ee1eSDan Willemsen if m.Has(fd) || fd.ContainingOneof() != nil { 182*1c12ee1eSDan Willemsen continue // ignore populated fields and fields within a oneofs 183*1c12ee1eSDan Willemsen } 184*1c12ee1eSDan Willemsen 185*1c12ee1eSDan Willemsen v := m.Get(fd) 186*1c12ee1eSDan Willemsen isProto2Scalar := fd.Syntax() == protoreflect.Proto2 && fd.Default().IsValid() 187*1c12ee1eSDan Willemsen isSingularMessage := fd.Cardinality() != protoreflect.Repeated && fd.Message() != nil 188*1c12ee1eSDan Willemsen if isProto2Scalar || isSingularMessage { 189*1c12ee1eSDan Willemsen v = protoreflect.Value{} // use invalid value to emit null 190*1c12ee1eSDan Willemsen } 191*1c12ee1eSDan Willemsen if !f(fd, v) { 192*1c12ee1eSDan Willemsen return 193*1c12ee1eSDan Willemsen } 194*1c12ee1eSDan Willemsen } 195*1c12ee1eSDan Willemsen m.Message.Range(f) 196*1c12ee1eSDan Willemsen} 197*1c12ee1eSDan Willemsen 198*1c12ee1eSDan Willemsen// marshalMessage marshals the fields in the given protoreflect.Message. 199*1c12ee1eSDan Willemsen// If the typeURL is non-empty, then a synthetic "@type" field is injected 200*1c12ee1eSDan Willemsen// containing the URL as the value. 201*1c12ee1eSDan Willemsenfunc (e encoder) marshalMessage(m protoreflect.Message, typeURL string) error { 202*1c12ee1eSDan Willemsen if !flags.ProtoLegacy && messageset.IsMessageSet(m.Descriptor()) { 203*1c12ee1eSDan Willemsen return errors.New("no support for proto1 MessageSets") 204*1c12ee1eSDan Willemsen } 205*1c12ee1eSDan Willemsen 206*1c12ee1eSDan Willemsen if marshal := wellKnownTypeMarshaler(m.Descriptor().FullName()); marshal != nil { 207*1c12ee1eSDan Willemsen return marshal(e, m) 208*1c12ee1eSDan Willemsen } 209*1c12ee1eSDan Willemsen 210*1c12ee1eSDan Willemsen e.StartObject() 211*1c12ee1eSDan Willemsen defer e.EndObject() 212*1c12ee1eSDan Willemsen 213*1c12ee1eSDan Willemsen var fields order.FieldRanger = m 214*1c12ee1eSDan Willemsen if e.opts.EmitUnpopulated { 215*1c12ee1eSDan Willemsen fields = unpopulatedFieldRanger{m} 216*1c12ee1eSDan Willemsen } 217*1c12ee1eSDan Willemsen if typeURL != "" { 218*1c12ee1eSDan Willemsen fields = typeURLFieldRanger{fields, typeURL} 219*1c12ee1eSDan Willemsen } 220*1c12ee1eSDan Willemsen 221*1c12ee1eSDan Willemsen var err error 222*1c12ee1eSDan Willemsen order.RangeFields(fields, order.IndexNameFieldOrder, func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { 223*1c12ee1eSDan Willemsen name := fd.JSONName() 224*1c12ee1eSDan Willemsen if e.opts.UseProtoNames { 225*1c12ee1eSDan Willemsen name = fd.TextName() 226*1c12ee1eSDan Willemsen } 227*1c12ee1eSDan Willemsen 228*1c12ee1eSDan Willemsen if err = e.WriteName(name); err != nil { 229*1c12ee1eSDan Willemsen return false 230*1c12ee1eSDan Willemsen } 231*1c12ee1eSDan Willemsen if err = e.marshalValue(v, fd); err != nil { 232*1c12ee1eSDan Willemsen return false 233*1c12ee1eSDan Willemsen } 234*1c12ee1eSDan Willemsen return true 235*1c12ee1eSDan Willemsen }) 236*1c12ee1eSDan Willemsen return err 237*1c12ee1eSDan Willemsen} 238*1c12ee1eSDan Willemsen 239*1c12ee1eSDan Willemsen// marshalValue marshals the given protoreflect.Value. 240*1c12ee1eSDan Willemsenfunc (e encoder) marshalValue(val protoreflect.Value, fd protoreflect.FieldDescriptor) error { 241*1c12ee1eSDan Willemsen switch { 242*1c12ee1eSDan Willemsen case fd.IsList(): 243*1c12ee1eSDan Willemsen return e.marshalList(val.List(), fd) 244*1c12ee1eSDan Willemsen case fd.IsMap(): 245*1c12ee1eSDan Willemsen return e.marshalMap(val.Map(), fd) 246*1c12ee1eSDan Willemsen default: 247*1c12ee1eSDan Willemsen return e.marshalSingular(val, fd) 248*1c12ee1eSDan Willemsen } 249*1c12ee1eSDan Willemsen} 250*1c12ee1eSDan Willemsen 251*1c12ee1eSDan Willemsen// marshalSingular marshals the given non-repeated field value. This includes 252*1c12ee1eSDan Willemsen// all scalar types, enums, messages, and groups. 253*1c12ee1eSDan Willemsenfunc (e encoder) marshalSingular(val protoreflect.Value, fd protoreflect.FieldDescriptor) error { 254*1c12ee1eSDan Willemsen if !val.IsValid() { 255*1c12ee1eSDan Willemsen e.WriteNull() 256*1c12ee1eSDan Willemsen return nil 257*1c12ee1eSDan Willemsen } 258*1c12ee1eSDan Willemsen 259*1c12ee1eSDan Willemsen switch kind := fd.Kind(); kind { 260*1c12ee1eSDan Willemsen case protoreflect.BoolKind: 261*1c12ee1eSDan Willemsen e.WriteBool(val.Bool()) 262*1c12ee1eSDan Willemsen 263*1c12ee1eSDan Willemsen case protoreflect.StringKind: 264*1c12ee1eSDan Willemsen if e.WriteString(val.String()) != nil { 265*1c12ee1eSDan Willemsen return errors.InvalidUTF8(string(fd.FullName())) 266*1c12ee1eSDan Willemsen } 267*1c12ee1eSDan Willemsen 268*1c12ee1eSDan Willemsen case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: 269*1c12ee1eSDan Willemsen e.WriteInt(val.Int()) 270*1c12ee1eSDan Willemsen 271*1c12ee1eSDan Willemsen case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: 272*1c12ee1eSDan Willemsen e.WriteUint(val.Uint()) 273*1c12ee1eSDan Willemsen 274*1c12ee1eSDan Willemsen case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Uint64Kind, 275*1c12ee1eSDan Willemsen protoreflect.Sfixed64Kind, protoreflect.Fixed64Kind: 276*1c12ee1eSDan Willemsen // 64-bit integers are written out as JSON string. 277*1c12ee1eSDan Willemsen e.WriteString(val.String()) 278*1c12ee1eSDan Willemsen 279*1c12ee1eSDan Willemsen case protoreflect.FloatKind: 280*1c12ee1eSDan Willemsen // Encoder.WriteFloat handles the special numbers NaN and infinites. 281*1c12ee1eSDan Willemsen e.WriteFloat(val.Float(), 32) 282*1c12ee1eSDan Willemsen 283*1c12ee1eSDan Willemsen case protoreflect.DoubleKind: 284*1c12ee1eSDan Willemsen // Encoder.WriteFloat handles the special numbers NaN and infinites. 285*1c12ee1eSDan Willemsen e.WriteFloat(val.Float(), 64) 286*1c12ee1eSDan Willemsen 287*1c12ee1eSDan Willemsen case protoreflect.BytesKind: 288*1c12ee1eSDan Willemsen e.WriteString(base64.StdEncoding.EncodeToString(val.Bytes())) 289*1c12ee1eSDan Willemsen 290*1c12ee1eSDan Willemsen case protoreflect.EnumKind: 291*1c12ee1eSDan Willemsen if fd.Enum().FullName() == genid.NullValue_enum_fullname { 292*1c12ee1eSDan Willemsen e.WriteNull() 293*1c12ee1eSDan Willemsen } else { 294*1c12ee1eSDan Willemsen desc := fd.Enum().Values().ByNumber(val.Enum()) 295*1c12ee1eSDan Willemsen if e.opts.UseEnumNumbers || desc == nil { 296*1c12ee1eSDan Willemsen e.WriteInt(int64(val.Enum())) 297*1c12ee1eSDan Willemsen } else { 298*1c12ee1eSDan Willemsen e.WriteString(string(desc.Name())) 299*1c12ee1eSDan Willemsen } 300*1c12ee1eSDan Willemsen } 301*1c12ee1eSDan Willemsen 302*1c12ee1eSDan Willemsen case protoreflect.MessageKind, protoreflect.GroupKind: 303*1c12ee1eSDan Willemsen if err := e.marshalMessage(val.Message(), ""); err != nil { 304*1c12ee1eSDan Willemsen return err 305*1c12ee1eSDan Willemsen } 306*1c12ee1eSDan Willemsen 307*1c12ee1eSDan Willemsen default: 308*1c12ee1eSDan Willemsen panic(fmt.Sprintf("%v has unknown kind: %v", fd.FullName(), kind)) 309*1c12ee1eSDan Willemsen } 310*1c12ee1eSDan Willemsen return nil 311*1c12ee1eSDan Willemsen} 312*1c12ee1eSDan Willemsen 313*1c12ee1eSDan Willemsen// marshalList marshals the given protoreflect.List. 314*1c12ee1eSDan Willemsenfunc (e encoder) marshalList(list protoreflect.List, fd protoreflect.FieldDescriptor) error { 315*1c12ee1eSDan Willemsen e.StartArray() 316*1c12ee1eSDan Willemsen defer e.EndArray() 317*1c12ee1eSDan Willemsen 318*1c12ee1eSDan Willemsen for i := 0; i < list.Len(); i++ { 319*1c12ee1eSDan Willemsen item := list.Get(i) 320*1c12ee1eSDan Willemsen if err := e.marshalSingular(item, fd); err != nil { 321*1c12ee1eSDan Willemsen return err 322*1c12ee1eSDan Willemsen } 323*1c12ee1eSDan Willemsen } 324*1c12ee1eSDan Willemsen return nil 325*1c12ee1eSDan Willemsen} 326*1c12ee1eSDan Willemsen 327*1c12ee1eSDan Willemsen// marshalMap marshals given protoreflect.Map. 328*1c12ee1eSDan Willemsenfunc (e encoder) marshalMap(mmap protoreflect.Map, fd protoreflect.FieldDescriptor) error { 329*1c12ee1eSDan Willemsen e.StartObject() 330*1c12ee1eSDan Willemsen defer e.EndObject() 331*1c12ee1eSDan Willemsen 332*1c12ee1eSDan Willemsen var err error 333*1c12ee1eSDan Willemsen order.RangeEntries(mmap, order.GenericKeyOrder, func(k protoreflect.MapKey, v protoreflect.Value) bool { 334*1c12ee1eSDan Willemsen if err = e.WriteName(k.String()); err != nil { 335*1c12ee1eSDan Willemsen return false 336*1c12ee1eSDan Willemsen } 337*1c12ee1eSDan Willemsen if err = e.marshalSingular(v, fd.MapValue()); err != nil { 338*1c12ee1eSDan Willemsen return false 339*1c12ee1eSDan Willemsen } 340*1c12ee1eSDan Willemsen return true 341*1c12ee1eSDan Willemsen }) 342*1c12ee1eSDan Willemsen return err 343*1c12ee1eSDan Willemsen} 344