encoding/protojson: ignore unknown enum name if DiscardUnknown=true

This makes encoding/protojson pass all protobuf conformance tests.

Other Similar CL:
  - https://go-review.googlesource.com/c/protobuf/+/350469
  - https://go-review.googlesource.com/c/protobuf/+/256677

Fixes golang/protobuf#1208

Change-Id: I9f74162b80a3c7ee4750160fc59f0313345046de
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/525535
Reviewed-by: Cassondra Foesch <cfoesch@gmail.com>
Reviewed-by: Michael Stapelberg <stapelberg@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
Singee 2023-09-05 17:28:35 +08:00 committed by Michael Stapelberg
parent f9212a8dfa
commit 70db1e1de2
3 changed files with 52 additions and 10 deletions

View File

@ -37,7 +37,7 @@ type UnmarshalOptions struct {
// required fields will not return an error.
AllowPartial bool
// If DiscardUnknown is set, unknown fields are ignored.
// If DiscardUnknown is set, unknown fields and enum name values are ignored.
DiscardUnknown bool
// Resolver is used for looking up types when unmarshaling
@ -266,7 +266,9 @@ func (d decoder) unmarshalSingular(m protoreflect.Message, fd protoreflect.Field
if err != nil {
return err
}
m.Set(fd, val)
if val.IsValid() {
m.Set(fd, val)
}
return nil
}
@ -329,7 +331,7 @@ func (d decoder) unmarshalScalar(fd protoreflect.FieldDescriptor) (protoreflect.
}
case protoreflect.EnumKind:
if v, ok := unmarshalEnum(tok, fd); ok {
if v, ok := unmarshalEnum(tok, fd, d.opts.DiscardUnknown); ok {
return v, nil
}
@ -474,7 +476,7 @@ func unmarshalBytes(tok json.Token) (protoreflect.Value, bool) {
return protoreflect.ValueOfBytes(b), true
}
func unmarshalEnum(tok json.Token, fd protoreflect.FieldDescriptor) (protoreflect.Value, bool) {
func unmarshalEnum(tok json.Token, fd protoreflect.FieldDescriptor, discardUnknown bool) (protoreflect.Value, bool) {
switch tok.Kind() {
case json.String:
// Lookup EnumNumber based on name.
@ -482,6 +484,9 @@ func unmarshalEnum(tok json.Token, fd protoreflect.FieldDescriptor) (protoreflec
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 {
@ -542,7 +547,9 @@ func (d decoder) unmarshalList(list protoreflect.List, fd protoreflect.FieldDesc
if err != nil {
return err
}
list.Append(val)
if val.IsValid() {
list.Append(val)
}
}
}
@ -609,8 +616,9 @@ Loop:
if err != nil {
return err
}
mmap.Set(pkey, pval)
if pval.IsValid() {
mmap.Set(pkey, pval)
}
}
return nil

View File

@ -2436,6 +2436,43 @@ func TestUnmarshal(t *testing.T) {
wantMessage: &anypb.Any{
TypeUrl: "type.googleapis.com/google.protobuf.Empty",
},
}, {
desc: "DiscardUnknown: unknown enum name",
inputMessage: &pb3.Enums{},
inputText: `{
"sEnum": "UNNAMED"
}`,
umo: protojson.UnmarshalOptions{DiscardUnknown: true},
wantMessage: &pb3.Enums{},
}, {
desc: "DiscardUnknown: repeated enum unknown name",
inputMessage: &pb2.Enums{},
inputText: `{
"rptEnum" : ["TEN", 1, 42, "UNNAMED"]
}`,
umo: protojson.UnmarshalOptions{DiscardUnknown: true},
wantMessage: &pb2.Enums{
RptEnum: []pb2.Enum{pb2.Enum_TEN, pb2.Enum_ONE, 42},
},
}, {
desc: "DiscardUnknown: enum map value unknown name",
inputMessage: &pb3.Maps{},
inputText: `{
"uint64ToEnum": {
"1" : "ONE",
"2" : 2,
"10": 101,
"3": "UNNAMED"
}
}`,
umo: protojson.UnmarshalOptions{DiscardUnknown: true},
wantMessage: &pb3.Maps{
Uint64ToEnum: map[uint64]pb3.Enum{
1: pb3.Enum_ONE,
2: pb3.Enum_TWO,
10: 101,
},
},
}, {
desc: "weak fields",
inputMessage: &testpb.TestWeak{},

View File

@ -1,3 +0,0 @@
Recommended.Proto3.JsonInput.IgnoreUnknownEnumStringValueInMapValue.ProtobufOutput
Recommended.Proto3.JsonInput.IgnoreUnknownEnumStringValueInOptionalField.ProtobufOutput
Recommended.Proto3.JsonInput.IgnoreUnknownEnumStringValueInRepeatedField.ProtobufOutput