| // Copyright 2015 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 jsonpb |
| |
| import ( |
| "encoding/json" |
| "errors" |
| "fmt" |
| "io" |
| "math" |
| "reflect" |
| "strconv" |
| "strings" |
| "time" |
| |
| "github.com/golang/protobuf/proto" |
| "google.golang.org/protobuf/encoding/protojson" |
| protoV2 "google.golang.org/protobuf/proto" |
| "google.golang.org/protobuf/reflect/protoreflect" |
| "google.golang.org/protobuf/reflect/protoregistry" |
| ) |
| |
| const wrapJSONUnmarshalV2 = false |
| |
| // UnmarshalNext unmarshals the next JSON object from d into m. |
| func UnmarshalNext(d *json.Decoder, m proto.Message) error { |
| return new(Unmarshaler).UnmarshalNext(d, m) |
| } |
| |
| // Unmarshal unmarshals a JSON object from r into m. |
| func Unmarshal(r io.Reader, m proto.Message) error { |
| return new(Unmarshaler).Unmarshal(r, m) |
| } |
| |
| // UnmarshalString unmarshals a JSON object from s into m. |
| func UnmarshalString(s string, m proto.Message) error { |
| return new(Unmarshaler).Unmarshal(strings.NewReader(s), m) |
| } |
| |
| // Unmarshaler is a configurable object for converting from a JSON |
| // representation to a protocol buffer object. |
| type Unmarshaler struct { |
| // AllowUnknownFields specifies whether to allow messages to contain |
| // unknown JSON fields, as opposed to failing to unmarshal. |
| AllowUnknownFields bool |
| |
| // AnyResolver is used to resolve the google.protobuf.Any well-known type. |
| // If unset, the global registry is used by default. |
| AnyResolver AnyResolver |
| } |
| |
| // JSONPBUnmarshaler is implemented by protobuf messages that customize the way |
| // they are unmarshaled from JSON. Messages that implement this should also |
| // implement JSONPBMarshaler so that the custom format can be produced. |
| // |
| // The JSON unmarshaling must follow the JSON to proto specification: |
| // https://developers.google.com/protocol-buffers/docs/proto3#json |
| // |
| // Deprecated: Custom types should implement protobuf reflection instead. |
| type JSONPBUnmarshaler interface { |
| UnmarshalJSONPB(*Unmarshaler, []byte) error |
| } |
| |
| // Unmarshal unmarshals a JSON object from r into m. |
| func (u *Unmarshaler) Unmarshal(r io.Reader, m proto.Message) error { |
| return u.UnmarshalNext(json.NewDecoder(r), m) |
| } |
| |
| // UnmarshalNext unmarshals the next JSON object from d into m. |
| func (u *Unmarshaler) UnmarshalNext(d *json.Decoder, m proto.Message) error { |
| if m == nil { |
| return errors.New("invalid nil message") |
| } |
| |
| // Parse the next JSON object from the stream. |
| raw := json.RawMessage{} |
| if err := d.Decode(&raw); err != nil { |
| return err |
| } |
| |
| // Check for custom unmarshalers first since they may not properly |
| // implement protobuf reflection that the logic below relies on. |
| if jsu, ok := m.(JSONPBUnmarshaler); ok { |
| return jsu.UnmarshalJSONPB(u, raw) |
| } |
| |
| mr := proto.MessageReflect(m) |
| |
| // NOTE: For historical reasons, a top-level null is treated as a noop. |
| // This is incorrect, but kept for compatibility. |
| if string(raw) == "null" && mr.Descriptor().FullName() != "google.protobuf.Value" { |
| return nil |
| } |
| |
| if wrapJSONUnmarshalV2 { |
| // NOTE: If input message is non-empty, we need to preserve merge semantics |
| // of the old jsonpb implementation. These semantics are not supported by |
| // the protobuf JSON specification. |
| isEmpty := true |
| mr.Range(func(protoreflect.FieldDescriptor, protoreflect.Value) bool { |
| isEmpty = false // at least one iteration implies non-empty |
| return false |
| }) |
| if !isEmpty { |
| // Perform unmarshaling into a newly allocated, empty message. |
| mr = mr.New() |
| |
| // Use a defer to copy all unmarshaled fields into the original message. |
| dst := proto.MessageReflect(m) |
| defer mr.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { |
| dst.Set(fd, v) |
| return true |
| }) |
| } |
| |
| // Unmarshal using the v2 JSON unmarshaler. |
| opts := protojson.UnmarshalOptions{ |
| DiscardUnknown: u.AllowUnknownFields, |
| } |
| if u.AnyResolver != nil { |
| opts.Resolver = anyResolver{u.AnyResolver} |
| } |
| return opts.Unmarshal(raw, mr.Interface()) |
| } else { |
| if err := u.unmarshalMessage(mr, raw); err != nil { |
| return err |
| } |
| return protoV2.CheckInitialized(mr.Interface()) |
| } |
| } |
| |
| func (u *Unmarshaler) unmarshalMessage(m protoreflect.Message, in []byte) error { |
| md := m.Descriptor() |
| fds := md.Fields() |
| |
| if jsu, ok := proto.MessageV1(m.Interface()).(JSONPBUnmarshaler); ok { |
| return jsu.UnmarshalJSONPB(u, in) |
| } |
| |
| if string(in) == "null" && md.FullName() != "google.protobuf.Value" { |
| return nil |
| } |
| |
| switch wellKnownType(md.FullName()) { |
| case "Any": |
| var jsonObject map[string]json.RawMessage |
| if err := json.Unmarshal(in, &jsonObject); err != nil { |
| return err |
| } |
| |
| rawTypeURL, ok := jsonObject["@type"] |
| if !ok { |
| return errors.New("Any JSON doesn't have '@type'") |
| } |
| typeURL, err := unquoteString(string(rawTypeURL)) |
| if err != nil { |
| return fmt.Errorf("can't unmarshal Any's '@type': %q", rawTypeURL) |
| } |
| m.Set(fds.ByNumber(1), protoreflect.ValueOfString(typeURL)) |
| |
| var m2 protoreflect.Message |
| if u.AnyResolver != nil { |
| mi, err := u.AnyResolver.Resolve(typeURL) |
| if err != nil { |
| return err |
| } |
| m2 = proto.MessageReflect(mi) |
| } else { |
| mt, err := protoregistry.GlobalTypes.FindMessageByURL(typeURL) |
| if err != nil { |
| if err == protoregistry.NotFound { |
| return fmt.Errorf("could not resolve Any message type: %v", typeURL) |
| } |
| return err |
| } |
| m2 = mt.New() |
| } |
| |
| if wellKnownType(m2.Descriptor().FullName()) != "" { |
| rawValue, ok := jsonObject["value"] |
| if !ok { |
| return errors.New("Any JSON doesn't have 'value'") |
| } |
| if err := u.unmarshalMessage(m2, rawValue); err != nil { |
| return fmt.Errorf("can't unmarshal Any nested proto %v: %v", typeURL, err) |
| } |
| } else { |
| delete(jsonObject, "@type") |
| rawJSON, err := json.Marshal(jsonObject) |
| if err != nil { |
| return fmt.Errorf("can't generate JSON for Any's nested proto to be unmarshaled: %v", err) |
| } |
| if err = u.unmarshalMessage(m2, rawJSON); err != nil { |
| return fmt.Errorf("can't unmarshal Any nested proto %v: %v", typeURL, err) |
| } |
| } |
| |
| rawWire, err := protoV2.Marshal(m2.Interface()) |
| if err != nil { |
| return fmt.Errorf("can't marshal proto %v into Any.Value: %v", typeURL, err) |
| } |
| m.Set(fds.ByNumber(2), protoreflect.ValueOfBytes(rawWire)) |
| return nil |
| case "BoolValue", "BytesValue", "StringValue", |
| "Int32Value", "UInt32Value", "FloatValue", |
| "Int64Value", "UInt64Value", "DoubleValue": |
| fd := fds.ByNumber(1) |
| v, err := u.unmarshalValue(m.NewField(fd), in, fd) |
| if err != nil { |
| return err |
| } |
| m.Set(fd, v) |
| return nil |
| case "Duration": |
| v, err := unquoteString(string(in)) |
| if err != nil { |
| return err |
| } |
| d, err := time.ParseDuration(v) |
| if err != nil { |
| return fmt.Errorf("bad Duration: %v", err) |
| } |
| |
| sec := d.Nanoseconds() / 1e9 |
| nsec := d.Nanoseconds() % 1e9 |
| m.Set(fds.ByNumber(1), protoreflect.ValueOfInt64(int64(sec))) |
| m.Set(fds.ByNumber(2), protoreflect.ValueOfInt32(int32(nsec))) |
| return nil |
| case "Timestamp": |
| v, err := unquoteString(string(in)) |
| if err != nil { |
| return err |
| } |
| t, err := time.Parse(time.RFC3339Nano, v) |
| if err != nil { |
| return fmt.Errorf("bad Timestamp: %v", err) |
| } |
| |
| sec := t.Unix() |
| nsec := t.Nanosecond() |
| m.Set(fds.ByNumber(1), protoreflect.ValueOfInt64(int64(sec))) |
| m.Set(fds.ByNumber(2), protoreflect.ValueOfInt32(int32(nsec))) |
| return nil |
| case "Value": |
| switch { |
| case string(in) == "null": |
| m.Set(fds.ByNumber(1), protoreflect.ValueOfEnum(0)) |
| case string(in) == "true": |
| m.Set(fds.ByNumber(4), protoreflect.ValueOfBool(true)) |
| case string(in) == "false": |
| m.Set(fds.ByNumber(4), protoreflect.ValueOfBool(false)) |
| case hasPrefixAndSuffix('"', in, '"'): |
| s, err := unquoteString(string(in)) |
| if err != nil { |
| return fmt.Errorf("unrecognized type for Value %q", in) |
| } |
| m.Set(fds.ByNumber(3), protoreflect.ValueOfString(s)) |
| case hasPrefixAndSuffix('[', in, ']'): |
| v := m.Mutable(fds.ByNumber(6)) |
| return u.unmarshalMessage(v.Message(), in) |
| case hasPrefixAndSuffix('{', in, '}'): |
| v := m.Mutable(fds.ByNumber(5)) |
| return u.unmarshalMessage(v.Message(), in) |
| default: |
| f, err := strconv.ParseFloat(string(in), 0) |
| if err != nil { |
| return fmt.Errorf("unrecognized type for Value %q", in) |
| } |
| m.Set(fds.ByNumber(2), protoreflect.ValueOfFloat64(f)) |
| } |
| return nil |
| case "ListValue": |
| var jsonArray []json.RawMessage |
| if err := json.Unmarshal(in, &jsonArray); err != nil { |
| return fmt.Errorf("bad ListValue: %v", err) |
| } |
| |
| lv := m.Mutable(fds.ByNumber(1)).List() |
| for _, raw := range jsonArray { |
| ve := lv.NewElement() |
| if err := u.unmarshalMessage(ve.Message(), raw); err != nil { |
| return err |
| } |
| lv.Append(ve) |
| } |
| return nil |
| case "Struct": |
| var jsonObject map[string]json.RawMessage |
| if err := json.Unmarshal(in, &jsonObject); err != nil { |
| return fmt.Errorf("bad StructValue: %v", err) |
| } |
| |
| mv := m.Mutable(fds.ByNumber(1)).Map() |
| for key, raw := range jsonObject { |
| kv := protoreflect.ValueOf(key).MapKey() |
| vv := mv.NewValue() |
| if err := u.unmarshalMessage(vv.Message(), raw); err != nil { |
| return fmt.Errorf("bad value in StructValue for key %q: %v", key, err) |
| } |
| mv.Set(kv, vv) |
| } |
| return nil |
| } |
| |
| var jsonObject map[string]json.RawMessage |
| if err := json.Unmarshal(in, &jsonObject); err != nil { |
| return err |
| } |
| |
| // Handle known fields. |
| for i := 0; i < fds.Len(); i++ { |
| fd := fds.Get(i) |
| if fd.IsWeak() && fd.Message().IsPlaceholder() { |
| continue // weak reference is not linked in |
| } |
| |
| // Search for any raw JSON value associated with this field. |
| var raw json.RawMessage |
| name := string(fd.Name()) |
| if fd.Kind() == protoreflect.GroupKind { |
| name = string(fd.Message().Name()) |
| } |
| if v, ok := jsonObject[name]; ok { |
| delete(jsonObject, name) |
| raw = v |
| } |
| name = string(fd.JSONName()) |
| if v, ok := jsonObject[name]; ok { |
| delete(jsonObject, name) |
| raw = v |
| } |
| |
| field := m.NewField(fd) |
| // Unmarshal the field value. |
| if raw == nil || (string(raw) == "null" && !isSingularWellKnownValue(fd) && !isSingularJSONPBUnmarshaler(field, fd)) { |
| continue |
| } |
| v, err := u.unmarshalValue(field, raw, fd) |
| if err != nil { |
| return err |
| } |
| m.Set(fd, v) |
| } |
| |
| // Handle extension fields. |
| for name, raw := range jsonObject { |
| if !strings.HasPrefix(name, "[") || !strings.HasSuffix(name, "]") { |
| continue |
| } |
| |
| // Resolve the extension field by name. |
| xname := protoreflect.FullName(name[len("[") : len(name)-len("]")]) |
| xt, _ := protoregistry.GlobalTypes.FindExtensionByName(xname) |
| if xt == nil && isMessageSet(md) { |
| xt, _ = protoregistry.GlobalTypes.FindExtensionByName(xname.Append("message_set_extension")) |
| } |
| if xt == nil { |
| continue |
| } |
| delete(jsonObject, name) |
| fd := xt.TypeDescriptor() |
| if fd.ContainingMessage().FullName() != m.Descriptor().FullName() { |
| return fmt.Errorf("extension field %q does not extend message %q", xname, m.Descriptor().FullName()) |
| } |
| |
| field := m.NewField(fd) |
| // Unmarshal the field value. |
| if raw == nil || (string(raw) == "null" && !isSingularWellKnownValue(fd) && !isSingularJSONPBUnmarshaler(field, fd)) { |
| continue |
| } |
| v, err := u.unmarshalValue(field, raw, fd) |
| if err != nil { |
| return err |
| } |
| m.Set(fd, v) |
| } |
| |
| if !u.AllowUnknownFields && len(jsonObject) > 0 { |
| for name := range jsonObject { |
| return fmt.Errorf("unknown field %q in %v", name, md.FullName()) |
| } |
| } |
| return nil |
| } |
| |
| func isSingularWellKnownValue(fd protoreflect.FieldDescriptor) bool { |
| if md := fd.Message(); md != nil { |
| return md.FullName() == "google.protobuf.Value" && fd.Cardinality() != protoreflect.Repeated |
| } |
| return false |
| } |
| |
| func isSingularJSONPBUnmarshaler(v protoreflect.Value, fd protoreflect.FieldDescriptor) bool { |
| if fd.Message() != nil && fd.Cardinality() != protoreflect.Repeated { |
| _, ok := proto.MessageV1(v.Interface()).(JSONPBUnmarshaler) |
| return ok |
| } |
| return false |
| } |
| |
| func (u *Unmarshaler) unmarshalValue(v protoreflect.Value, in []byte, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) { |
| switch { |
| case fd.IsList(): |
| var jsonArray []json.RawMessage |
| if err := json.Unmarshal(in, &jsonArray); err != nil { |
| return v, err |
| } |
| lv := v.List() |
| for _, raw := range jsonArray { |
| ve, err := u.unmarshalSingularValue(lv.NewElement(), raw, fd) |
| if err != nil { |
| return v, err |
| } |
| lv.Append(ve) |
| } |
| return v, nil |
| case fd.IsMap(): |
| var jsonObject map[string]json.RawMessage |
| if err := json.Unmarshal(in, &jsonObject); err != nil { |
| return v, err |
| } |
| kfd := fd.MapKey() |
| vfd := fd.MapValue() |
| mv := v.Map() |
| for key, raw := range jsonObject { |
| var kv protoreflect.MapKey |
| if kfd.Kind() == protoreflect.StringKind { |
| kv = protoreflect.ValueOf(key).MapKey() |
| } else { |
| v, err := u.unmarshalSingularValue(kfd.Default(), []byte(key), kfd) |
| if err != nil { |
| return v, err |
| } |
| kv = v.MapKey() |
| } |
| |
| vv, err := u.unmarshalSingularValue(mv.NewValue(), raw, vfd) |
| if err != nil { |
| return v, err |
| } |
| mv.Set(kv, vv) |
| } |
| return v, nil |
| default: |
| return u.unmarshalSingularValue(v, in, fd) |
| } |
| } |
| |
| var nonFinite = map[string]float64{ |
| `"NaN"`: math.NaN(), |
| `"Infinity"`: math.Inf(+1), |
| `"-Infinity"`: math.Inf(-1), |
| } |
| |
| func (u *Unmarshaler) unmarshalSingularValue(v protoreflect.Value, in []byte, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) { |
| switch fd.Kind() { |
| case protoreflect.BoolKind: |
| return unmarshalValue(in, new(bool)) |
| case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: |
| return unmarshalValue(trimQuote(in), new(int32)) |
| case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: |
| return unmarshalValue(trimQuote(in), new(int64)) |
| case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: |
| return unmarshalValue(trimQuote(in), new(uint32)) |
| case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: |
| return unmarshalValue(trimQuote(in), new(uint64)) |
| case protoreflect.FloatKind: |
| if f, ok := nonFinite[string(in)]; ok { |
| return protoreflect.ValueOfFloat32(float32(f)), nil |
| } |
| return unmarshalValue(trimQuote(in), new(float32)) |
| case protoreflect.DoubleKind: |
| if f, ok := nonFinite[string(in)]; ok { |
| return protoreflect.ValueOfFloat64(float64(f)), nil |
| } |
| return unmarshalValue(trimQuote(in), new(float64)) |
| case protoreflect.StringKind: |
| return unmarshalValue(in, new(string)) |
| case protoreflect.BytesKind: |
| return unmarshalValue(in, new([]byte)) |
| case protoreflect.EnumKind: |
| if hasPrefixAndSuffix('"', in, '"') { |
| vd := fd.Enum().Values().ByName(protoreflect.Name(trimQuote(in))) |
| if vd == nil { |
| return v, fmt.Errorf("unknown value %q for enum %s", in, fd.Enum().FullName()) |
| } |
| return protoreflect.ValueOfEnum(vd.Number()), nil |
| } |
| return unmarshalValue(in, new(protoreflect.EnumNumber)) |
| case protoreflect.MessageKind, protoreflect.GroupKind: |
| err := u.unmarshalMessage(v.Message(), in) |
| return v, err |
| default: |
| panic(fmt.Sprintf("invalid kind %v", fd.Kind())) |
| } |
| } |
| |
| func unmarshalValue(in []byte, v interface{}) (protoreflect.Value, error) { |
| err := json.Unmarshal(in, v) |
| return protoreflect.ValueOf(reflect.ValueOf(v).Elem().Interface()), err |
| } |
| |
| func unquoteString(in string) (out string, err error) { |
| err = json.Unmarshal([]byte(in), &out) |
| return out, err |
| } |
| |
| func hasPrefixAndSuffix(prefix byte, in []byte, suffix byte) bool { |
| if len(in) >= 2 && in[0] == prefix && in[len(in)-1] == suffix { |
| return true |
| } |
| return false |
| } |
| |
| // trimQuote is like unquoteString but simply strips surrounding quotes. |
| // This is incorrect, but is behavior done by the legacy implementation. |
| func trimQuote(in []byte) []byte { |
| if len(in) >= 2 && in[0] == '"' && in[len(in)-1] == '"' { |
| in = in[1 : len(in)-1] |
| } |
| return in |
| } |