encoding/jsonpb: fix unmarshaling of NullValue field
A JSON "null" field should set the NullValue enum field because
NullValue has the custom encoding format of "null".
Change-Id: I2bfa0900de64d7e2874f7c6db04b1cbc0b61b904
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/170107
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/encoding/jsonpb/decode.go b/encoding/jsonpb/decode.go
index 8511a8d..7655054 100644
--- a/encoding/jsonpb/decode.go
+++ b/encoding/jsonpb/decode.go
@@ -225,8 +225,8 @@
seenNums.Set(num)
// No need to set values for JSON null unless the field type is
- // google.protobuf.Value.
- if o.decoder.Peek() == json.Null && !isKnownValue(fd) {
+ // google.protobuf.Value or google.protobuf.NullValue.
+ if o.decoder.Peek() == json.Null && !isKnownValue(fd) && !isNullValue(fd) {
o.decoder.Read()
continue
}
@@ -277,6 +277,16 @@
return nil, protoregistry.NotFound
}
+func isKnownValue(fd pref.FieldDescriptor) bool {
+ md := fd.MessageType()
+ return md != nil && md.FullName() == "google.protobuf.Value"
+}
+
+func isNullValue(fd pref.FieldDescriptor) bool {
+ ed := fd.EnumType()
+ return ed != nil && ed.FullName() == "google.protobuf.NullValue"
+}
+
// unmarshalSingular unmarshals to the non-repeated field specified by the given
// FieldDescriptor.
func (o UnmarshalOptions) unmarshalSingular(knownFields pref.KnownFields, fd pref.FieldDescriptor) error {
@@ -509,6 +519,12 @@
return pref.Value{}, err
}
return pref.ValueOf(pref.EnumNumber(n)), nil
+
+ case json.Null:
+ // This is only valid for google.protobuf.NullValue.
+ if isNullValue(fd) {
+ return pref.ValueOf(pref.EnumNumber(0)), nil
+ }
}
return pref.Value{}, unexpectedJSONError{jval}
diff --git a/encoding/jsonpb/decode_test.go b/encoding/jsonpb/decode_test.go
index 1e9bd98..e7f4239 100644
--- a/encoding/jsonpb/decode_test.go
+++ b/encoding/jsonpb/decode_test.go
@@ -439,24 +439,31 @@
desc: "enum set to number string",
inputMessage: &pb3.Enums{},
inputText: `{
- "sEnum": "1",
+ "sEnum": "1"
}`,
wantErr: true,
}, {
desc: "enum set to invalid named",
inputMessage: &pb3.Enums{},
inputText: `{
- "sEnum": "UNNAMED",
+ "sEnum": "UNNAMED"
}`,
wantErr: true,
}, {
desc: "enum set to not enum",
inputMessage: &pb3.Enums{},
inputText: `{
- "sEnum": true,
+ "sEnum": true
}`,
wantErr: true,
}, {
+ desc: "enum set to JSON null",
+ inputMessage: &pb3.Enums{},
+ inputText: `{
+ "sEnum": null
+}`,
+ wantMessage: &pb3.Enums{},
+ }, {
desc: "proto name",
inputMessage: &pb3.JSONNames{},
inputText: `{
@@ -1478,6 +1485,20 @@
},
wantErr: true,
}, {
+ desc: "NullValue field with JSON null",
+ inputMessage: &pb2.KnownTypes{},
+ inputText: `{
+ "optNull": null
+}`,
+ wantMessage: &pb2.KnownTypes{OptNull: new(knownpb.NullValue)},
+ }, {
+ desc: "NullValue field with string",
+ inputMessage: &pb2.KnownTypes{},
+ inputText: `{
+ "optNull": "NULL_VALUE"
+}`,
+ wantMessage: &pb2.KnownTypes{OptNull: new(knownpb.NullValue)},
+ }, {
desc: "BytesValue",
inputMessage: &knownpb.BytesValue{},
inputText: `"aGVsbG8="`,
diff --git a/encoding/jsonpb/encode_test.go b/encoding/jsonpb/encode_test.go
index 005de06..96e21f2 100644
--- a/encoding/jsonpb/encode_test.go
+++ b/encoding/jsonpb/encode_test.go
@@ -1177,6 +1177,12 @@
input: &knownpb.Empty{},
want: `{}`,
}, {
+ desc: "NullValue field",
+ input: &pb2.KnownTypes{OptNull: new(knownpb.NullValue)},
+ want: `{
+ "optNull": null
+}`,
+ }, {
desc: "Value empty",
input: &knownpb.Value{},
wantErr: true,
diff --git a/encoding/jsonpb/well_known_types.go b/encoding/jsonpb/well_known_types.go
index db1e365..788e314 100644
--- a/encoding/jsonpb/well_known_types.go
+++ b/encoding/jsonpb/well_known_types.go
@@ -547,11 +547,6 @@
return errors.New("%s: none of the variants is set", msgType.FullName())
}
-func isKnownValue(fd pref.FieldDescriptor) bool {
- md := fd.MessageType()
- return md != nil && md.FullName() == "google.protobuf.Value"
-}
-
func (o UnmarshalOptions) unmarshalKnownValue(m pref.Message) error {
var nerr errors.NonFatal
knownFields := m.KnownFields()