Add support for decoding a stream of JSON objects. Signed-off-by: David Symonds <dsymonds@golang.org>
diff --git a/jsonpb/jsonpb.go b/jsonpb/jsonpb.go index 768564e..a9e778c 100644 --- a/jsonpb/jsonpb.go +++ b/jsonpb/jsonpb.go
@@ -428,15 +428,23 @@ return out.err } +// UnmarshalNext unmarshals the next protocol buffer from a JSON object stream. +// This function is lenient and will decode any options permutations of the +// related Marshaler. +func UnmarshalNext(dec *json.Decoder, pb proto.Message) error { + inputValue := json.RawMessage{} + if err := dec.Decode(&inputValue); err != nil { + return err + } + return unmarshalValue(reflect.ValueOf(pb).Elem(), inputValue) +} + // Unmarshal unmarshals a JSON object stream into a protocol // buffer. This function is lenient and will decode any options // permutations of the related Marshaler. func Unmarshal(r io.Reader, pb proto.Message) error { - inputValue := json.RawMessage{} - if err := json.NewDecoder(r).Decode(&inputValue); err != nil { - return err - } - return unmarshalValue(reflect.ValueOf(pb).Elem(), inputValue) + dec := json.NewDecoder(r) + return UnmarshalNext(dec, pb) } // UnmarshalString will populate the fields of a protocol buffer based
diff --git a/jsonpb/jsonpb_test.go b/jsonpb/jsonpb_test.go index 3d70b87..c04da85 100644 --- a/jsonpb/jsonpb_test.go +++ b/jsonpb/jsonpb_test.go
@@ -32,6 +32,9 @@ package jsonpb import ( + "bytes" + "encoding/json" + "io" "reflect" "testing" @@ -415,6 +418,39 @@ } } +func TestUnmarshalNext(t *testing.T) { + // Create a buffer with many concatenated JSON objects. + var b bytes.Buffer + for _, tt := range unmarshalingTests { + b.WriteString(tt.json) + } + + dec := json.NewDecoder(&b) + for _, tt := range unmarshalingTests { + // Make a new instance of the type of our expected object. + p := reflect.New(reflect.TypeOf(tt.pb).Elem()).Interface().(proto.Message) + + err := UnmarshalNext(dec, p) + if err != nil { + t.Errorf("%s: %v", tt.desc, err) + continue + } + + // For easier diffs, compare text strings of the protos. + exp := proto.MarshalTextString(tt.pb) + act := proto.MarshalTextString(p) + if string(exp) != string(act) { + t.Errorf("%s: got [%s] want [%s]", tt.desc, act, exp) + } + } + + p := &pb.Simple{} + err := UnmarshalNext(dec, p) + if err != io.EOF { + t.Errorf("eof: got %v, expected io.EOF", err) + } +} + var unmarshalingShouldError = []struct { desc string in string