diff --git a/encoding/protojson/decode_test.go b/encoding/protojson/decode_test.go index faf5eb548..40e52b36b 100644 --- a/encoding/protojson/decode_test.go +++ b/encoding/protojson/decode_test.go @@ -2197,6 +2197,18 @@ func TestUnmarshal(t *testing.T) { wantMessage: &anypb.Any{ TypeUrl: "type.googleapis.com/google.protobuf.Empty", }, + }, { + desc: "Any with Empty and no value and AllowPartial", + umo: protojson.UnmarshalOptions{AllowPartial: true}, + inputMessage: &anypb.Any{}, + inputText: `{ + "@type": "type.googleapis.com/google.protobuf.Empty" +}`, + wantMessage: func() proto.Message { + return &anypb.Any{ + TypeUrl: "type.googleapis.com/google.protobuf.Empty", + } + }(), }, { desc: "Any with missing Empty", inputMessage: &anypb.Any{}, diff --git a/encoding/protojson/well_known_types.go b/encoding/protojson/well_known_types.go index 4b177c820..9a2c104d8 100644 --- a/encoding/protojson/well_known_types.go +++ b/encoding/protojson/well_known_types.go @@ -176,7 +176,7 @@ func (d decoder) unmarshalAny(m protoreflect.Message) error { // Use another decoder to parse the unread bytes for @type field. This // avoids advancing a read from current decoder because the current JSON // object may contain the fields of the embedded type. - dec := decoder{d.Clone(), UnmarshalOptions{RecursionLimit: d.opts.RecursionLimit}} + dec := decoder{d.Clone(), UnmarshalOptions{RecursionLimit: d.opts.RecursionLimit, AllowPartial: d.opts.AllowPartial}} tok, err := findTypeURL(dec) switch err { case errEmptyObject: @@ -348,7 +348,9 @@ func (d decoder) unmarshalAnyValue(unmarshal unmarshalFunc, m protoreflect.Messa switch tok.Kind() { case json.ObjectClose: if !found { - return d.newError(tok.Pos(), `missing "value" field`) + if !d.opts.AllowPartial { + return d.newError(tok.Pos(), `missing "value" field`) + } } return nil