diff --git a/runtime/marshal_json_test.go b/runtime/marshal_json_test.go index 6ab9699e48c..94280990be2 100644 --- a/runtime/marshal_json_test.go +++ b/runtime/marshal_json_test.go @@ -204,6 +204,13 @@ var ( {data: (*string)(nil), json: "null"}, {data: new(empty.Empty), json: "{}"}, {data: examplepb.NumericEnum_ONE, json: "1"}, + {data: nil, json: "null"}, + {data: (*string)(nil), json: "null"}, + {data: []interface{}{nil, "foo", -1, 1.234, true}, json: `[null,"foo",-1,1.234,true]`}, + { + data: map[string]interface{}{"bar": nil, "baz": -1, "fiz": 1.234, "foo": true}, + json: `{"bar":null,"baz":-1,"fiz":1.234,"foo":true}`, + }, { data: (*examplepb.NumericEnum)(proto.Int32(int32(examplepb.NumericEnum_ONE))), json: "1", diff --git a/runtime/marshal_jsonpb.go b/runtime/marshal_jsonpb.go index 0a0d130bba6..2a90a4403cf 100644 --- a/runtime/marshal_jsonpb.go +++ b/runtime/marshal_jsonpb.go @@ -21,9 +21,7 @@ func (*JSONPb) ContentType() string { return "application/json" } -// Marshal marshals "v" into JSON -// Currently it can marshal only proto.Message. -// TODO(yugui) Support fields of primitive types in a message. +// Marshal marshals "v" into JSON. func (j *JSONPb) Marshal(v interface{}) ([]byte, error) { if _, ok := v.(proto.Message); !ok { return j.marshalNonProtoField(v) @@ -55,6 +53,9 @@ func (j *JSONPb) marshalTo(w io.Writer, v interface{}) error { // i.e. primitive types, enums; pointers to primitives or enums; maps from // integer/string types to primitives/enums/pointers to messages. func (j *JSONPb) marshalNonProtoField(v interface{}) ([]byte, error) { + if v == nil { + return []byte("null"), nil + } rv := reflect.ValueOf(v) for rv.Kind() == reflect.Ptr { if rv.IsNil() { diff --git a/runtime/marshal_jsonpb_test.go b/runtime/marshal_jsonpb_test.go index 679283b4511..0c3d01bfeba 100644 --- a/runtime/marshal_jsonpb_test.go +++ b/runtime/marshal_jsonpb_test.go @@ -121,25 +121,23 @@ func TestJSONPbMarshal(t *testing.T) { func TestJSONPbMarshalFields(t *testing.T) { var m runtime.JSONPb - for _, spec := range []struct { - val interface{} - want string - }{} { - buf, err := m.Marshal(spec.val) + m.EnumsAsInts = true // builtin fixtures include an enum, expected to be marshaled as int + for _, spec := range builtinFieldFixtures { + buf, err := m.Marshal(spec.data) if err != nil { - t.Errorf("m.Marshal(%#v) failed with %v; want success", spec.val, err) + t.Errorf("m.Marshal(%#v) failed with %v; want success", spec.data, err) } - if got, want := string(buf), spec.want; got != want { - t.Errorf("m.Marshal(%#v) = %q; want %q", spec.val, got, want) + if got, want := string(buf), spec.json; got != want { + t.Errorf("m.Marshal(%#v) = %q; want %q", spec.data, got, want) } } - m.EnumsAsInts = true + m.EnumsAsInts = false buf, err := m.Marshal(examplepb.NumericEnum_ONE) if err != nil { t.Errorf("m.Marshal(%#v) failed with %v; want success", examplepb.NumericEnum_ONE, err) } - if got, want := string(buf), "1"; got != want { + if got, want := string(buf), `"ONE"`; got != want { t.Errorf("m.Marshal(%#v) = %q; want %q", examplepb.NumericEnum_ONE, got, want) } }