Skip to content

Commit

Permalink
merge 001690d39bd620847bb265d93a7c5e1bd3737308 and 62e4364d64b32762fe…
Browse files Browse the repository at this point in the history
…bb61f2c88c0a29bc49a225 from golang/protobuf
  • Loading branch information
awalterschulze committed Jul 18, 2016
1 parent 2752d97 commit b5aec62
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 133 deletions.
65 changes: 52 additions & 13 deletions jsonpb/jsonpb.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ type Marshaler struct {
// value, and for newlines to be appear between fields and array
// elements.
Indent string

// Whether to use the original (.proto) name for fields.
OrigName bool
}

// Marshal marshals a protocol buffer into JSON.
Expand Down Expand Up @@ -145,7 +148,7 @@ func (m *Marshaler) marshalObject(out *errWriter, v proto.Message, indent string
value = sv.Field(0)
valueField = sv.Type().Field(0)
}
prop := jsonProperties(valueField)
prop := jsonProperties(valueField, m.OrigName)
if !firstField {
m.writeSep(out)
}
Expand Down Expand Up @@ -194,7 +197,7 @@ func (m *Marshaler) marshalObject(out *errWriter, v proto.Message, indent string
value := reflect.ValueOf(ext)
var prop proto.Properties
prop.Parse(desc.Tag)
prop.OrigName = fmt.Sprintf("[%s]", desc.Name)
prop.JSONName = fmt.Sprintf("[%s]", desc.Name)
if !firstField {
m.writeSep(out)
}
Expand Down Expand Up @@ -229,7 +232,7 @@ func (m *Marshaler) marshalField(out *errWriter, prop *proto.Properties, v refle
out.write(m.Indent)
}
out.write(`"`)
out.write(prop.OrigName)
out.write(prop.JSONName)
out.write(`":`)
if m.Indent != "" {
out.write(" ")
Expand Down Expand Up @@ -439,19 +442,38 @@ func unmarshalValue(target reflect.Value, inputValue json.RawMessage) error {
return err
}

consumeField := func(prop *proto.Properties) (json.RawMessage, bool) {
// Be liberal in what names we accept; both orig_name and camelName are okay.
fieldNames := acceptedJSONFieldNames(prop)

vOrig, okOrig := jsonFields[fieldNames.orig]
vCamel, okCamel := jsonFields[fieldNames.camel]
if !okOrig && !okCamel {
return nil, false
}
// If, for some reason, both are present in the data, favour the camelName.
var raw json.RawMessage
if okOrig {
raw = vOrig
delete(jsonFields, fieldNames.orig)
}
if okCamel {
raw = vCamel
delete(jsonFields, fieldNames.camel)
}
return raw, true
}

sprops := proto.GetProperties(targetType)
for i := 0; i < target.NumField(); i++ {
ft := target.Type().Field(i)
if strings.HasPrefix(ft.Name, "XXX_") {
continue
}
fieldName := jsonProperties(ft).OrigName

valueForField, ok := jsonFields[fieldName]
valueForField, ok := consumeField(sprops.Prop[i])
if !ok {
continue
}
delete(jsonFields, fieldName)

// Handle enums, which have an underlying type of int32,
// and may appear as strings. We do this while handling
Expand All @@ -475,20 +497,22 @@ func unmarshalValue(target reflect.Value, inputValue json.RawMessage) error {
f.SetInt(int64(n))
continue
}

if err := unmarshalValue(target.Field(i), valueForField); err != nil {
return err
}
}
// Check for any oneof fields.
for fname, raw := range jsonFields {
if oop, ok := sprops.OneofTypes[fname]; ok {
if len(jsonFields) > 0 {
for _, oop := range sprops.OneofTypes {
raw, ok := consumeField(oop.Prop)
if !ok {
continue
}
nv := reflect.New(oop.Type.Elem())
target.Field(oop.Field).Set(nv)
if err := unmarshalValue(nv.Elem().Field(0), raw); err != nil {
return err
}
delete(jsonFields, fname)
}
}
if len(jsonFields) > 0 {
Expand Down Expand Up @@ -577,13 +601,28 @@ func unmarshalValue(target reflect.Value, inputValue json.RawMessage) error {
return json.Unmarshal(inputValue, target.Addr().Interface())
}

// jsonProperties returns parsed proto.Properties for the field.
func jsonProperties(f reflect.StructField) *proto.Properties {
// jsonProperties returns parsed proto.Properties for the field and corrects JSONName attribute.
func jsonProperties(f reflect.StructField, origName bool) *proto.Properties {
var prop proto.Properties
prop.Init(f.Type, f.Name, f.Tag.Get("protobuf"), &f)
if origName || prop.JSONName == "" {
prop.JSONName = prop.OrigName
}
return &prop
}

type fieldNames struct {
orig, camel string
}

func acceptedJSONFieldNames(prop *proto.Properties) fieldNames {
opts := fieldNames{orig: prop.OrigName, camel: prop.OrigName}
if prop.JSONName != "" {
opts.camel = prop.JSONName
}
return opts
}

// extendableProto is an interface implemented by any protocol buffer that may be extended.
type extendableProto interface {
proto.Message
Expand Down
139 changes: 72 additions & 67 deletions jsonpb/jsonpb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,33 +63,33 @@ var (
}

simpleObjectJSON = `{` +
`"o_bool":true,` +
`"o_int32":-32,` +
`"o_int64":"-6400000000",` +
`"o_uint32":32,` +
`"o_uint64":"6400000000",` +
`"o_sint32":-13,` +
`"o_sint64":"-2600000000",` +
`"o_float":3.14,` +
`"o_double":6.02214179e+23,` +
`"o_string":"hello \"there\"",` +
`"o_bytes":"YmVlcCBib29w",` +
`"o_cast_bytes":"d293"` +
`"oBool":true,` +
`"oInt32":-32,` +
`"oInt64":"-6400000000",` +
`"oUint32":32,` +
`"oUint64":"6400000000",` +
`"oSint32":-13,` +
`"oSint64":"-2600000000",` +
`"oFloat":3.14,` +
`"oDouble":6.02214179e+23,` +
`"oString":"hello \"there\"",` +
`"oBytes":"YmVlcCBib29w",` +
`"oCastBytes":"d293"` +
`}`

simpleObjectPrettyJSON = `{
"o_bool": true,
"o_int32": -32,
"o_int64": "-6400000000",
"o_uint32": 32,
"o_uint64": "6400000000",
"o_sint32": -13,
"o_sint64": "-2600000000",
"o_float": 3.14,
"o_double": 6.02214179e+23,
"o_string": "hello \"there\"",
"o_bytes": "YmVlcCBib29w",
"o_cast_bytes": "d293"
"oBool": true,
"oInt32": -32,
"oInt64": "-6400000000",
"oUint32": 32,
"oUint64": "6400000000",
"oSint32": -13,
"oSint64": "-2600000000",
"oFloat": 3.14,
"oDouble": 6.02214179e+23,
"oString": "hello \"there\"",
"oBytes": "YmVlcCBib29w",
"oCastBytes": "d293"
}`

repeatsObject = &pb.Repeats{
Expand All @@ -107,65 +107,65 @@ var (
}

repeatsObjectJSON = `{` +
`"r_bool":[true,false,true],` +
`"r_int32":[-3,-4,-5],` +
`"r_int64":["-123456789","-987654321"],` +
`"r_uint32":[1,2,3],` +
`"r_uint64":["6789012345","3456789012"],` +
`"r_sint32":[-1,-2,-3],` +
`"r_sint64":["-6789012345","-3456789012"],` +
`"r_float":[3.14,6.28],` +
`"r_double":[2.99792458e+08,6.62606957e-34],` +
`"r_string":["happy","days"],` +
`"r_bytes":["c2tpdHRsZXM=","bSZtJ3M="]` +
`"rBool":[true,false,true],` +
`"rInt32":[-3,-4,-5],` +
`"rInt64":["-123456789","-987654321"],` +
`"rUint32":[1,2,3],` +
`"rUint64":["6789012345","3456789012"],` +
`"rSint32":[-1,-2,-3],` +
`"rSint64":["-6789012345","-3456789012"],` +
`"rFloat":[3.14,6.28],` +
`"rDouble":[2.99792458e+08,6.62606957e-34],` +
`"rString":["happy","days"],` +
`"rBytes":["c2tpdHRsZXM=","bSZtJ3M="]` +
`}`

repeatsObjectPrettyJSON = `{
"r_bool": [
"rBool": [
true,
false,
true
],
"r_int32": [
"rInt32": [
-3,
-4,
-5
],
"r_int64": [
"rInt64": [
"-123456789",
"-987654321"
],
"r_uint32": [
"rUint32": [
1,
2,
3
],
"r_uint64": [
"rUint64": [
"6789012345",
"3456789012"
],
"r_sint32": [
"rSint32": [
-1,
-2,
-3
],
"r_sint64": [
"rSint64": [
"-6789012345",
"-3456789012"
],
"r_float": [
"rFloat": [
3.14,
6.28
],
"r_double": [
"rDouble": [
2.99792458e+08,
6.62606957e-34
],
"r_string": [
"rString": [
"happy",
"days"
],
"r_bytes": [
"rBytes": [
"c2tpdHRsZXM=",
"bSZtJ3M="
]
Expand All @@ -185,46 +185,46 @@ var (
}

complexObjectJSON = `{"color":"GREEN",` +
`"r_color":["RED","GREEN","BLUE"],` +
`"simple":{"o_int32":-32},` +
`"r_simple":[{"o_int32":-32},{"o_int64":"25"}],` +
`"repeats":{"r_string":["roses","red"]},` +
`"r_repeats":[{"r_string":["roses","red"]},{"r_string":["violets","blue"]}]` +
`"rColor":["RED","GREEN","BLUE"],` +
`"simple":{"oInt32":-32},` +
`"rSimple":[{"oInt32":-32},{"oInt64":"25"}],` +
`"repeats":{"rString":["roses","red"]},` +
`"rRepeats":[{"rString":["roses","red"]},{"rString":["violets","blue"]}]` +
`}`

complexObjectPrettyJSON = `{
"color": "GREEN",
"r_color": [
"rColor": [
"RED",
"GREEN",
"BLUE"
],
"simple": {
"o_int32": -32
"oInt32": -32
},
"r_simple": [
"rSimple": [
{
"o_int32": -32
"oInt32": -32
},
{
"o_int64": "25"
"oInt64": "25"
}
],
"repeats": {
"r_string": [
"rString": [
"roses",
"red"
]
},
"r_repeats": [
"rRepeats": [
{
"r_string": [
"rString": [
"roses",
"red"
]
},
{
"r_string": [
"rString": [
"violets",
"blue"
]
Expand All @@ -238,7 +238,7 @@ var (

colorListPrettyJSON = `{
"color": 1000,
"r_color": [
"rColor": [
"RED"
]
}`
Expand Down Expand Up @@ -309,12 +309,14 @@ var marshalingTests = []struct {
`{"buggy":{"1234":"yup"}}`},
{"map<bool, bool>", marshaler, &pb.Mappy{Booly: map[bool]bool{false: true}}, `{"booly":{"false":true}}`},
{"proto2 map<int64, string>", marshaler, &pb.Maps{MInt64Str: map[int64]string{213: "cat"}},
`{"m_int64_str":{"213":"cat"}}`},
`{"mInt64Str":{"213":"cat"}}`},
{"proto2 map<bool, Object>", marshaler,
&pb.Maps{MBoolSimple: map[bool]*pb.Simple{true: {OInt32: proto.Int32(1)}}},
`{"m_bool_simple":{"true":{"o_int32":1}}}`},
`{"mBoolSimple":{"true":{"oInt32":1}}}`},
{"oneof, not set", marshaler, &pb.MsgWithOneof{}, `{}`},
{"oneof, set", marshaler, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Title{Title: "Grand Poobah"}}, `{"title":"Grand Poobah"}`},
{"oneof, set", marshaler, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Title{"Grand Poobah"}}, `{"title":"Grand Poobah"}`},
{"force orig_name", Marshaler{OrigName: true}, &pb.Simple{OInt32: proto.Int32(4)},
`{"o_int32":4}`},
{"proto2 extension", marshaler, realNumber, realNumberJSON},
}

Expand Down Expand Up @@ -347,19 +349,22 @@ var unmarshalingTests = []struct {
{"unknown enum value object",
"{\n \"color\": 1000,\n \"r_color\": [\n \"RED\"\n ]\n}",
&pb.Widget{Color: pb.Widget_Color(1000).Enum(), RColor: []pb.Widget_Color{pb.Widget_RED}}},
{"unquoted int64 object", `{"o_int64":-314}`, &pb.Simple{OInt64: proto.Int64(-314)}},
{"unquoted uint64 object", `{"o_uint64":123}`, &pb.Simple{OUint64: proto.Uint64(123)}},
{"unquoted int64 object", `{"oInt64":-314}`, &pb.Simple{OInt64: proto.Int64(-314)}},
{"unquoted uint64 object", `{"oUint64":123}`, &pb.Simple{OUint64: proto.Uint64(123)}},
{"map<int64, int32>", `{"nummy":{"1":2,"3":4}}`, &pb.Mappy{Nummy: map[int64]int32{1: 2, 3: 4}}},
{"map<string, string>", `{"strry":{"\"one\"":"two","three":"four"}}`, &pb.Mappy{Strry: map[string]string{`"one"`: "two", "three": "four"}}},
{"map<int32, Object>", `{"objjy":{"1":{"dub":1}}}`, &pb.Mappy{Objjy: map[int32]*pb.Simple3{1: {Dub: 1}}}},
{"oneof", `{"salary":31000}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Salary{Salary: 31000}}},
{"oneof spec name", `{"country":"Australia"}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Country{Country: "Australia"}}},
{"oneof orig_name", `{"Country":"Australia"}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Country{Country: "Australia"}}},
{"orig_name input", `{"o_bool":true}`, &pb.Simple{OBool: proto.Bool(true)}},
{"camelName input", `{"oBool":true}`, &pb.Simple{OBool: proto.Bool(true)}},
}

func TestUnmarshaling(t *testing.T) {
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 := UnmarshalString(tt.json, p)
if err != nil {
t.Error(err)
Expand Down
Loading

0 comments on commit b5aec62

Please sign in to comment.