diff --git a/jsonpb/jsonpb.go b/jsonpb/jsonpb.go index 29bca020ff..e93211b2dc 100644 --- a/jsonpb/jsonpb.go +++ b/jsonpb/jsonpb.go @@ -590,6 +590,10 @@ type Unmarshaler struct { // Whether to allow messages to contain unknown fields, as opposed to // failing to unmarshal. AllowUnknownFields bool + + // If non-nil, AllowedUnknownField will be called for each unknown field + // encountered and allowed during the unmarshalling. + AllowedUnknownField func(fieldName string) } // UnmarshalNext unmarshals the next protocol buffer from a JSON object stream. @@ -901,14 +905,21 @@ func (u *Unmarshaler) unmarshalValue(target reflect.Value, inputValue json.RawMe } } } - if !u.AllowUnknownFields && len(jsonFields) > 0 { - // Pick any field to be the scapegoat. - var f string - for fname := range jsonFields { - f = fname - break + if len(jsonFields) > 0 { + if !u.AllowUnknownFields { + // Pick any field to be the scapegoat. + var f string + for fname := range jsonFields { + f = fname + break + } + return fmt.Errorf("unknown field %q in %v", f, targetType) + } + if u.AllowedUnknownField != nil { + for fname := range jsonFields { + u.AllowedUnknownField(fname) + } } - return fmt.Errorf("unknown field %q in %v", f, targetType) } return nil } diff --git a/jsonpb/jsonpb_test.go b/jsonpb/jsonpb_test.go index 254caa6c45..873d29cdfd 100644 --- a/jsonpb/jsonpb_test.go +++ b/jsonpb/jsonpb_test.go @@ -37,6 +37,7 @@ import ( "io" "math" "reflect" + "sort" "strings" "testing" @@ -665,6 +666,25 @@ func TestUnmarshalNullObject(t *testing.T) { } } +func TestUnmarshalAllowedUnknownFields(t *testing.T) { + var msg pb.Simple + var unknownFields []string + unmarshaler := &Unmarshaler{ + AllowUnknownFields: true, + AllowedUnknownField: func(fname string) { + unknownFields = append(unknownFields, fname) + }, + } + json := `{"oBool": true, "unknown-a": null, "unknown-b": null}` + if err := unmarshaler.Unmarshal(strings.NewReader(json), &msg); err != nil { + t.Fatal(err) + } + sort.Strings(unknownFields) // order of fields is unspecified + if want := []string{"unknown-a", "unknown-b"}; !reflect.DeepEqual(unknownFields, want) { + t.Errorf("got %v want %v", unknownFields, want) + } +} + func TestUnmarshalNext(t *testing.T) { // We only need to check against a few, not all of them. tests := unmarshalingTests[:5]