Parcourir la source

jsonpb: fix unmarshal null to value that implements JSONPBUnmarshaler (#429)

Unmarshal should use JSONPBUnmarshaler even for JSON null value.
Xiao Zhang il y a 8 ans
Parent
commit
130e6b02ab
2 fichiers modifiés avec 50 ajouts et 1 suppressions
  1. 2 1
      jsonpb/jsonpb.go
  2. 48 0
      jsonpb/jsonpb_test.go

+ 2 - 1
jsonpb/jsonpb.go

@@ -673,7 +673,8 @@ func (u *Unmarshaler) unmarshalValue(target reflect.Value, inputValue json.RawMe
 	if targetType.Kind() == reflect.Ptr {
 		// If input value is "null" and target is a pointer type, then the field should be treated as not set
 		// UNLESS the target is structpb.Value, in which case it should be set to structpb.NullValue.
-		if string(inputValue) == "null" && targetType != reflect.TypeOf(&stpb.Value{}) {
+		_, isJSONPBUnmarshaler := target.Interface().(JSONPBUnmarshaler)
+		if string(inputValue) == "null" && targetType != reflect.TypeOf(&stpb.Value{}) && !isJSONPBUnmarshaler {
 			return nil
 		}
 		target.Set(reflect.New(targetType.Elem()))

+ 48 - 0
jsonpb/jsonpb_test.go

@@ -791,6 +791,19 @@ func TestUnmarshalJSONPBUnmarshaler(t *testing.T) {
 	}
 }
 
+func TestUnmarshalNullWithJSONPBUnmarshaler(t *testing.T) {
+	rawJson := `{"stringField":null}`
+	var ptrFieldMsg ptrFieldMessage
+	if err := Unmarshal(strings.NewReader(rawJson), &ptrFieldMsg); err != nil {
+		t.Errorf("unmarshal error: %v", err)
+	}
+
+	want := ptrFieldMessage{StringField: &stringField{IsSet: true, StringValue: "null"}}
+	if !proto.Equal(&ptrFieldMsg, &want) {
+		t.Errorf("unmarshal result StringField: got %v, want %v", ptrFieldMsg, want)
+	}
+}
+
 func TestUnmarshalAnyJSONPBUnmarshaler(t *testing.T) {
 	rawJson := `{ "@type": "blah.com/` + dynamicMessageName + `", "foo": "bar", "baz": [0, 1, 2, 3] }`
 	var got anypb.Any
@@ -821,6 +834,41 @@ func init() {
 	proto.RegisterType((*dynamicMessage)(nil), dynamicMessageName)
 }
 
+type ptrFieldMessage struct {
+	StringField *stringField `protobuf:"bytes,1,opt,name=stringField"`
+}
+
+func (m *ptrFieldMessage) Reset() {
+}
+
+func (m *ptrFieldMessage) String() string {
+	return m.StringField.StringValue
+}
+
+func (m *ptrFieldMessage) ProtoMessage() {
+}
+
+type stringField struct {
+	IsSet       bool   `protobuf:"varint,1,opt,name=isSet"`
+	StringValue string `protobuf:"bytes,2,opt,name=stringValue"`
+}
+
+func (s *stringField) Reset() {
+}
+
+func (s *stringField) String() string {
+	return s.StringValue
+}
+
+func (s *stringField) ProtoMessage() {
+}
+
+func (s *stringField) UnmarshalJSONPB(jum *Unmarshaler, js []byte) error {
+	s.IsSet = true
+	s.StringValue = string(js)
+	return nil
+}
+
 // dynamicMessage implements protobuf.Message but is not a normal generated message type.
 // It provides implementations of JSONPBMarshaler and JSONPBUnmarshaler for JSON support.
 type dynamicMessage struct {