| // Copyright 2019 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package protojson | |
| import ( | |
| "encoding/base64" | |
| "fmt" | |
| "math" | |
| "strconv" | |
| "strings" | |
| "google.golang.org/protobuf/encoding/protowire" | |
| "google.golang.org/protobuf/internal/encoding/json" | |
| "google.golang.org/protobuf/internal/encoding/messageset" | |
| "google.golang.org/protobuf/internal/errors" | |
| "google.golang.org/protobuf/internal/flags" | |
| "google.golang.org/protobuf/internal/genid" | |
| "google.golang.org/protobuf/internal/pragma" | |
| "google.golang.org/protobuf/internal/set" | |
| "google.golang.org/protobuf/proto" | |
| "google.golang.org/protobuf/reflect/protoreflect" | |
| "google.golang.org/protobuf/reflect/protoregistry" | |
| ) | |
| // Unmarshal reads the given []byte into the given [proto.Message]. | |
| // The provided message must be mutable (e.g., a non-nil pointer to a message). | |
| func Unmarshal(b []byte, m proto.Message) error { | |
| return UnmarshalOptions{}.Unmarshal(b, m) | |
| } | |
| // UnmarshalOptions is a configurable JSON format parser. | |
| type UnmarshalOptions struct { | |
| pragma.NoUnkeyedLiterals | |
| // If AllowPartial is set, input for messages that will result in missing | |
| // required fields will not return an error. | |
| AllowPartial bool | |
| // If DiscardUnknown is set, unknown fields and enum name values are ignored. | |
| DiscardUnknown bool | |
| // Resolver is used for looking up types when unmarshaling | |
| // google.protobuf.Any messages or extension fields. | |
| // If nil, this defaults to using protoregistry.GlobalTypes. | |
| Resolver interface { | |
| protoregistry.MessageTypeResolver | |
| protoregistry.ExtensionTypeResolver | |
| } | |
| // RecursionLimit limits how deeply messages may be nested. | |
| // If zero, a default limit is applied. | |
| RecursionLimit int | |
| } | |
| // Unmarshal reads the given []byte and populates the given [proto.Message] | |
| // using options in the UnmarshalOptions object. | |
| // It will clear the message first before setting the fields. | |
| // If it returns an error, the given message may be partially set. | |
| // The provided message must be mutable (e.g., a non-nil pointer to a message). | |
| func (o UnmarshalOptions) Unmarshal(b []byte, m proto.Message) error { | |
| return o.unmarshal(b, m) | |
| } | |
| // unmarshal is a centralized function that all unmarshal operations go through. | |
| // For profiling purposes, avoid changing the name of this function or | |
| // introducing other code paths for unmarshal that do not go through this. | |
| func (o UnmarshalOptions) unmarshal(b []byte, m proto.Message) error { | |
| proto.Reset(m) | |
| if o.Resolver == nil { | |
| o.Resolver = protoregistry.GlobalTypes | |
| } | |
| if o.RecursionLimit == 0 { | |
| o.RecursionLimit = protowire.DefaultRecursionLimit | |
| } | |
| dec := decoder{json.NewDecoder(b), o} | |
| if err := dec.unmarshalMessage(m.ProtoReflect(), false); err != nil { | |
| return err | |
| } | |
| // Check for EOF. | |
| tok, err := dec.Read() | |
| if err != nil { | |
| return err | |
| } | |
| if tok.Kind() != json.EOF { | |
| return dec.unexpectedTokenError(tok) | |
| } | |
| if o.AllowPartial { | |
| return nil | |
| } | |
| return proto.CheckInitialized(m) | |
| } | |
| type decoder struct { | |
| *json.Decoder | |
| opts UnmarshalOptions | |
| } | |
| // newError returns an error object with position info. | |
| func (d decoder) newError(pos int, f string, x ...any) error { | |
| line, column := d.Position(pos) | |
| head := fmt.Sprintf("(line %d:%d): ", line, column) | |
| return errors.New(head+f, x...) | |
| } | |
| // unexpectedTokenError returns a syntax error for the given unexpected token. | |
| func (d decoder) unexpectedTokenError(tok json.Token) error { | |
| return d.syntaxError(tok.Pos(), "unexpected token %s", tok.RawString()) | |
| } | |
| // syntaxError returns a syntax error for given position. | |
| func (d decoder) syntaxError(pos int, f string, x ...any) error { | |
| line, column := d.Position(pos) | |
| head := fmt.Sprintf("syntax error (line %d:%d): ", line, column) | |
| return errors.New(head+f, x...) | |
| } | |
| // unmarshalMessage unmarshals a message into the given protoreflect.Message. | |
| func (d decoder) unmarshalMessage(m protoreflect.Message, skipTypeURL bool) error { | |
| d.opts.RecursionLimit-- | |
| if d.opts.RecursionLimit < 0 { | |
| return errors.New("exceeded max recursion depth") | |
| } | |
| if unmarshal := wellKnownTypeUnmarshaler(m.Descriptor().FullName()); unmarshal != nil { | |
| return unmarshal(d, m) | |
| } | |
| tok, err := d.Read() | |
| if err != nil { | |
| return err | |
| } | |
| if tok.Kind() != json.ObjectOpen { | |
| return d.unexpectedTokenError(tok) | |
| } | |
| messageDesc := m.Descriptor() | |
| if !flags.ProtoLegacy && messageset.IsMessageSet(messageDesc) { | |
| return errors.New("no support for proto1 MessageSets") | |
| } | |
| var seenNums set.Ints | |
| var seenOneofs set.Ints | |
| fieldDescs := messageDesc.Fields() | |
| for { | |
| // Read field name. | |
| tok, err := d.Read() | |
| if err != nil { | |
| return err | |
| } | |
| switch tok.Kind() { | |
| default: | |
| return d.unexpectedTokenError(tok) | |
| case json.ObjectClose: | |
| return nil | |
| case json.Name: | |
| // Continue below. | |
| } | |
| name := tok.Name() | |
| // Unmarshaling a non-custom embedded message in Any will contain the | |
| // JSON field "@type" which should be skipped because it is not a field | |
| // of the embedded message, but simply an artifact of the Any format. | |
| if skipTypeURL && name == "@type" { | |
| d.Read() | |
| continue | |
| } | |
| // Get the FieldDescriptor. | |
| var fd protoreflect.FieldDescriptor | |
| if strings.HasPrefix(name, "[") && strings.HasSuffix(name, "]") { | |
| // Only extension names are in [name] format. | |
| extName := protoreflect.FullName(name[1 : len(name)-1]) | |
| extType, err := d.opts.Resolver.FindExtensionByName(extName) | |
| if err != nil && err != protoregistry.NotFound { | |
| return d.newError(tok.Pos(), "unable to resolve %s: %v", tok.RawString(), err) | |
| } | |
| if extType != nil { | |
| fd = extType.TypeDescriptor() | |
| if !messageDesc.ExtensionRanges().Has(fd.Number()) || fd.ContainingMessage().FullName() != messageDesc.FullName() { | |
| return d.newError(tok.Pos(), "message %v cannot be extended by %v", messageDesc.FullName(), fd.FullName()) | |
| } | |
| } | |
| } else { | |
| // The name can either be the JSON name or the proto field name. | |
| fd = fieldDescs.ByJSONName(name) | |
| if fd == nil { | |
| fd = fieldDescs.ByTextName(name) | |
| } | |
| } | |
| if fd == nil { | |
| // Field is unknown. | |
| if d.opts.DiscardUnknown { | |
| if err := d.skipJSONValue(); err != nil { | |
| return err | |
| } | |
| continue | |
| } | |
| return d.newError(tok.Pos(), "unknown field %v", tok.RawString()) | |
| } | |
| // Do not allow duplicate fields. | |
| num := uint64(fd.Number()) | |
| if seenNums.Has(num) { | |
| return d.newError(tok.Pos(), "duplicate field %v", tok.RawString()) | |
| } | |
| seenNums.Set(num) | |
| // No need to set values for JSON null unless the field type is | |
| // google.protobuf.Value or google.protobuf.NullValue. | |
| if tok, _ := d.Peek(); tok.Kind() == json.Null && !isKnownValue(fd) && !isNullValue(fd) { | |
| d.Read() | |
| continue | |
| } | |
| switch { | |
| case fd.IsList(): | |
| list := m.Mutable(fd).List() | |
| if err := d.unmarshalList(list, fd); err != nil { | |
| return err | |
| } | |
| case fd.IsMap(): | |
| mmap := m.Mutable(fd).Map() | |
| if err := d.unmarshalMap(mmap, fd); err != nil { | |
| return err | |
| } | |
| default: | |
| // If field is a oneof, check if it has already been set. | |
| if od := fd.ContainingOneof(); od != nil { | |
| idx := uint64(od.Index()) | |
| if seenOneofs.Has(idx) { | |
| return d.newError(tok.Pos(), "error parsing %s, oneof %v is already set", tok.RawString(), od.FullName()) | |
| } | |
| seenOneofs.Set(idx) | |
| } | |
| // Required or optional fields. | |
| if err := d.unmarshalSingular(m, fd); err != nil { | |
| return err | |
| } | |
| } | |
| } | |
| } | |
| func isKnownValue(fd protoreflect.FieldDescriptor) bool { | |
| md := fd.Message() | |
| return md != nil && md.FullName() == genid.Value_message_fullname | |
| } | |
| func isNullValue(fd protoreflect.FieldDescriptor) bool { | |
| ed := fd.Enum() | |
| return ed != nil && ed.FullName() == genid.NullValue_enum_fullname | |
| } | |
| // unmarshalSingular unmarshals to the non-repeated field specified | |
| // by the given FieldDescriptor. | |
| func (d decoder) unmarshalSingular(m protoreflect.Message, fd protoreflect.FieldDescriptor) error { | |
| var val protoreflect.Value | |
| var err error | |
| switch fd.Kind() { | |
| case protoreflect.MessageKind, protoreflect.GroupKind: | |
| val = m.NewField(fd) | |
| err = d.unmarshalMessage(val.Message(), false) | |
| default: | |
| val, err = d.unmarshalScalar(fd) | |
| } | |
| if err != nil { | |
| return err | |
| } | |
| if val.IsValid() { | |
| m.Set(fd, val) | |
| } | |
| return nil | |
| } | |
| // unmarshalScalar unmarshals to a scalar/enum protoreflect.Value specified by | |
| // the given FieldDescriptor. | |
| func (d decoder) unmarshalScalar(fd protoreflect.FieldDescriptor) (protoreflect.Value, error) { | |
| const b32 int = 32 | |
| const b64 int = 64 | |
| tok, err := d.Read() | |
| if err != nil { | |
| return protoreflect.Value{}, err | |
| } | |
| kind := fd.Kind() | |
| switch kind { | |
| case protoreflect.BoolKind: | |
| if tok.Kind() == json.Bool { | |
| return protoreflect.ValueOfBool(tok.Bool()), nil | |
| } | |
| case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: | |
| if v, ok := unmarshalInt(tok, b32); ok { | |
| return v, nil | |
| } | |
| case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: | |
| if v, ok := unmarshalInt(tok, b64); ok { | |
| return v, nil | |
| } | |
| case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: | |
| if v, ok := unmarshalUint(tok, b32); ok { | |
| return v, nil | |
| } | |
| case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: | |
| if v, ok := unmarshalUint(tok, b64); ok { | |
| return v, nil | |
| } | |
| case protoreflect.FloatKind: | |
| if v, ok := unmarshalFloat(tok, b32); ok { | |
| return v, nil | |
| } | |
| case protoreflect.DoubleKind: | |
| if v, ok := unmarshalFloat(tok, b64); ok { | |
| return v, nil | |
| } | |
| case protoreflect.StringKind: | |
| if tok.Kind() == json.String { | |
| return protoreflect.ValueOfString(tok.ParsedString()), nil | |
| } | |
| case protoreflect.BytesKind: | |
| if v, ok := unmarshalBytes(tok); ok { | |
| return v, nil | |
| } | |
| case protoreflect.EnumKind: | |
| if v, ok := unmarshalEnum(tok, fd, d.opts.DiscardUnknown); ok { | |
| return v, nil | |
| } | |
| default: | |
| panic(fmt.Sprintf("unmarshalScalar: invalid scalar kind %v", kind)) | |
| } | |
| return protoreflect.Value{}, d.newError(tok.Pos(), "invalid value for %v field %v: %v", kind, fd.JSONName(), tok.RawString()) | |
| } | |
| func unmarshalInt(tok json.Token, bitSize int) (protoreflect.Value, bool) { | |
| switch tok.Kind() { | |
| case json.Number: | |
| return getInt(tok, bitSize) | |
| case json.String: | |
| // Decode number from string. | |
| s := strings.TrimSpace(tok.ParsedString()) | |
| if len(s) != len(tok.ParsedString()) { | |
| return protoreflect.Value{}, false | |
| } | |
| dec := json.NewDecoder([]byte(s)) | |
| tok, err := dec.Read() | |
| if err != nil { | |
| return protoreflect.Value{}, false | |
| } | |
| return getInt(tok, bitSize) | |
| } | |
| return protoreflect.Value{}, false | |
| } | |
| func getInt(tok json.Token, bitSize int) (protoreflect.Value, bool) { | |
| n, ok := tok.Int(bitSize) | |
| if !ok { | |
| return protoreflect.Value{}, false | |
| } | |
| if bitSize == 32 { | |
| return protoreflect.ValueOfInt32(int32(n)), true | |
| } | |
| return protoreflect.ValueOfInt64(n), true | |
| } | |
| func unmarshalUint(tok json.Token, bitSize int) (protoreflect.Value, bool) { | |
| switch tok.Kind() { | |
| case json.Number: | |
| return getUint(tok, bitSize) | |
| case json.String: | |
| // Decode number from string. | |
| s := strings.TrimSpace(tok.ParsedString()) | |
| if len(s) != len(tok.ParsedString()) { | |
| return protoreflect.Value{}, false | |
| } | |
| dec := json.NewDecoder([]byte(s)) | |
| tok, err := dec.Read() | |
| if err != nil { | |
| return protoreflect.Value{}, false | |
| } | |
| return getUint(tok, bitSize) | |
| } | |
| return protoreflect.Value{}, false | |
| } | |
| func getUint(tok json.Token, bitSize int) (protoreflect.Value, bool) { | |
| n, ok := tok.Uint(bitSize) | |
| if !ok { | |
| return protoreflect.Value{}, false | |
| } | |
| if bitSize == 32 { | |
| return protoreflect.ValueOfUint32(uint32(n)), true | |
| } | |
| return protoreflect.ValueOfUint64(n), true | |
| } | |
| func unmarshalFloat(tok json.Token, bitSize int) (protoreflect.Value, bool) { | |
| switch tok.Kind() { | |
| case json.Number: | |
| return getFloat(tok, bitSize) | |
| case json.String: | |
| s := tok.ParsedString() | |
| switch s { | |
| case "NaN": | |
| if bitSize == 32 { | |
| return protoreflect.ValueOfFloat32(float32(math.NaN())), true | |
| } | |
| return protoreflect.ValueOfFloat64(math.NaN()), true | |
| case "Infinity": | |
| if bitSize == 32 { | |
| return protoreflect.ValueOfFloat32(float32(math.Inf(+1))), true | |
| } | |
| return protoreflect.ValueOfFloat64(math.Inf(+1)), true | |
| case "-Infinity": | |
| if bitSize == 32 { | |
| return protoreflect.ValueOfFloat32(float32(math.Inf(-1))), true | |
| } | |
| return protoreflect.ValueOfFloat64(math.Inf(-1)), true | |
| } | |
| // Decode number from string. | |
| if len(s) != len(strings.TrimSpace(s)) { | |
| return protoreflect.Value{}, false | |
| } | |
| dec := json.NewDecoder([]byte(s)) | |
| tok, err := dec.Read() | |
| if err != nil { | |
| return protoreflect.Value{}, false | |
| } | |
| return getFloat(tok, bitSize) | |
| } | |
| return protoreflect.Value{}, false | |
| } | |
| func getFloat(tok json.Token, bitSize int) (protoreflect.Value, bool) { | |
| n, ok := tok.Float(bitSize) | |
| if !ok { | |
| return protoreflect.Value{}, false | |
| } | |
| if bitSize == 32 { | |
| return protoreflect.ValueOfFloat32(float32(n)), true | |
| } | |
| return protoreflect.ValueOfFloat64(n), true | |
| } | |
| func unmarshalBytes(tok json.Token) (protoreflect.Value, bool) { | |
| if tok.Kind() != json.String { | |
| return protoreflect.Value{}, false | |
| } | |
| s := tok.ParsedString() | |
| enc := base64.StdEncoding | |
| if strings.ContainsAny(s, "-_") { | |
| enc = base64.URLEncoding | |
| } | |
| if len(s)%4 != 0 { | |
| enc = enc.WithPadding(base64.NoPadding) | |
| } | |
| b, err := enc.DecodeString(s) | |
| if err != nil { | |
| return protoreflect.Value{}, false | |
| } | |
| return protoreflect.ValueOfBytes(b), true | |
| } | |
| func unmarshalEnum(tok json.Token, fd protoreflect.FieldDescriptor, discardUnknown bool) (protoreflect.Value, bool) { | |
| switch tok.Kind() { | |
| case json.String: | |
| // Lookup EnumNumber based on name. | |
| s := tok.ParsedString() | |
| if enumVal := fd.Enum().Values().ByName(protoreflect.Name(s)); enumVal != nil { | |
| return protoreflect.ValueOfEnum(enumVal.Number()), true | |
| } | |
| if discardUnknown { | |
| return protoreflect.Value{}, true | |
| } | |
| case json.Number: | |
| if n, ok := tok.Int(32); ok { | |
| return protoreflect.ValueOfEnum(protoreflect.EnumNumber(n)), true | |
| } | |
| case json.Null: | |
| // This is only valid for google.protobuf.NullValue. | |
| if isNullValue(fd) { | |
| return protoreflect.ValueOfEnum(0), true | |
| } | |
| } | |
| return protoreflect.Value{}, false | |
| } | |
| func (d decoder) unmarshalList(list protoreflect.List, fd protoreflect.FieldDescriptor) error { | |
| tok, err := d.Read() | |
| if err != nil { | |
| return err | |
| } | |
| if tok.Kind() != json.ArrayOpen { | |
| return d.unexpectedTokenError(tok) | |
| } | |
| switch fd.Kind() { | |
| case protoreflect.MessageKind, protoreflect.GroupKind: | |
| for { | |
| tok, err := d.Peek() | |
| if err != nil { | |
| return err | |
| } | |
| if tok.Kind() == json.ArrayClose { | |
| d.Read() | |
| return nil | |
| } | |
| val := list.NewElement() | |
| if err := d.unmarshalMessage(val.Message(), false); err != nil { | |
| return err | |
| } | |
| list.Append(val) | |
| } | |
| default: | |
| for { | |
| tok, err := d.Peek() | |
| if err != nil { | |
| return err | |
| } | |
| if tok.Kind() == json.ArrayClose { | |
| d.Read() | |
| return nil | |
| } | |
| val, err := d.unmarshalScalar(fd) | |
| if err != nil { | |
| return err | |
| } | |
| if val.IsValid() { | |
| list.Append(val) | |
| } | |
| } | |
| } | |
| return nil | |
| } | |
| func (d decoder) unmarshalMap(mmap protoreflect.Map, fd protoreflect.FieldDescriptor) error { | |
| tok, err := d.Read() | |
| if err != nil { | |
| return err | |
| } | |
| if tok.Kind() != json.ObjectOpen { | |
| return d.unexpectedTokenError(tok) | |
| } | |
| // Determine ahead whether map entry is a scalar type or a message type in | |
| // order to call the appropriate unmarshalMapValue func inside the for loop | |
| // below. | |
| var unmarshalMapValue func() (protoreflect.Value, error) | |
| switch fd.MapValue().Kind() { | |
| case protoreflect.MessageKind, protoreflect.GroupKind: | |
| unmarshalMapValue = func() (protoreflect.Value, error) { | |
| val := mmap.NewValue() | |
| if err := d.unmarshalMessage(val.Message(), false); err != nil { | |
| return protoreflect.Value{}, err | |
| } | |
| return val, nil | |
| } | |
| default: | |
| unmarshalMapValue = func() (protoreflect.Value, error) { | |
| return d.unmarshalScalar(fd.MapValue()) | |
| } | |
| } | |
| Loop: | |
| for { | |
| // Read field name. | |
| tok, err := d.Read() | |
| if err != nil { | |
| return err | |
| } | |
| switch tok.Kind() { | |
| default: | |
| return d.unexpectedTokenError(tok) | |
| case json.ObjectClose: | |
| break Loop | |
| case json.Name: | |
| // Continue. | |
| } | |
| // Unmarshal field name. | |
| pkey, err := d.unmarshalMapKey(tok, fd.MapKey()) | |
| if err != nil { | |
| return err | |
| } | |
| // Check for duplicate field name. | |
| if mmap.Has(pkey) { | |
| return d.newError(tok.Pos(), "duplicate map key %v", tok.RawString()) | |
| } | |
| // Read and unmarshal field value. | |
| pval, err := unmarshalMapValue() | |
| if err != nil { | |
| return err | |
| } | |
| if pval.IsValid() { | |
| mmap.Set(pkey, pval) | |
| } | |
| } | |
| return nil | |
| } | |
| // unmarshalMapKey converts given token of Name kind into a protoreflect.MapKey. | |
| // A map key type is any integral or string type. | |
| func (d decoder) unmarshalMapKey(tok json.Token, fd protoreflect.FieldDescriptor) (protoreflect.MapKey, error) { | |
| const b32 = 32 | |
| const b64 = 64 | |
| const base10 = 10 | |
| name := tok.Name() | |
| kind := fd.Kind() | |
| switch kind { | |
| case protoreflect.StringKind: | |
| return protoreflect.ValueOfString(name).MapKey(), nil | |
| case protoreflect.BoolKind: | |
| switch name { | |
| case "true": | |
| return protoreflect.ValueOfBool(true).MapKey(), nil | |
| case "false": | |
| return protoreflect.ValueOfBool(false).MapKey(), nil | |
| } | |
| case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: | |
| if n, err := strconv.ParseInt(name, base10, b32); err == nil { | |
| return protoreflect.ValueOfInt32(int32(n)).MapKey(), nil | |
| } | |
| case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: | |
| if n, err := strconv.ParseInt(name, base10, b64); err == nil { | |
| return protoreflect.ValueOfInt64(int64(n)).MapKey(), nil | |
| } | |
| case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: | |
| if n, err := strconv.ParseUint(name, base10, b32); err == nil { | |
| return protoreflect.ValueOfUint32(uint32(n)).MapKey(), nil | |
| } | |
| case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: | |
| if n, err := strconv.ParseUint(name, base10, b64); err == nil { | |
| return protoreflect.ValueOfUint64(uint64(n)).MapKey(), nil | |
| } | |
| default: | |
| panic(fmt.Sprintf("invalid kind for map key: %v", kind)) | |
| } | |
| return protoreflect.MapKey{}, d.newError(tok.Pos(), "invalid value for %v key: %s", kind, tok.RawString()) | |
| } |