jsonpb: Don't emit zero value proto3 fields by default. This aligns the default behaviour with the spec (https://developers.google.com/protocol-buffers/docs/proto3#json), but also adds a Marshaler option to control it. Fixes #122.
diff --git a/jsonpb/jsonpb.go b/jsonpb/jsonpb.go index 4c22d21..3522ac2 100644 --- a/jsonpb/jsonpb.go +++ b/jsonpb/jsonpb.go
@@ -56,11 +56,14 @@ ) // Marshaler is a configurable object for converting between -// protocol buffer objects and a JSON representation for them +// protocol buffer objects and a JSON representation for them. type Marshaler struct { // Whether to render enum values as integers, as opposed to string values. EnumsAsInts bool + // Whether to render fields with zero values. + EmitDefaults bool + // A string to indent each level by. The presence of this field will // also cause a space to appear between the field separator and // value, and for newlines to be appear between fields and array @@ -106,8 +109,6 @@ continue } - // TODO: proto3 objects should have default values omitted. - // IsNil will panic on most value kinds. switch value.Kind() { case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: @@ -116,6 +117,31 @@ } } + if !m.EmitDefaults { + switch value.Kind() { + case reflect.Bool: + if !value.Bool() { + continue + } + case reflect.Int32, reflect.Int64: + if value.Int() == 0 { + continue + } + case reflect.Uint32, reflect.Uint64: + if value.Uint() == 0 { + continue + } + case reflect.Float32, reflect.Float64: + if value.Float() == 0 { + continue + } + case reflect.String: + if value.Len() == 0 { + continue + } + } + } + // Oneof fields need special handling. if valueField.Tag.Get("protobuf_oneof") != "" { // value is an interface containing &T{real_value}.
diff --git a/jsonpb/jsonpb_test.go b/jsonpb/jsonpb_test.go index 0a5b4ec..97a648e 100644 --- a/jsonpb/jsonpb_test.go +++ b/jsonpb/jsonpb_test.go
@@ -291,7 +291,8 @@ &pb.Widget{Color: pb.Widget_BLUE.Enum()}, colorPrettyJSON}, {"unknown enum value object", marshalerAllOptions, &pb.Widget{Color: pb.Widget_Color(1000).Enum(), RColor: []pb.Widget_Color{pb.Widget_RED}}, colorListPrettyJSON}, - {"proto3 object with empty value", marshaler, &pb.Simple3{}, `{"dub":0}`}, + {"empty value", marshaler, &pb.Simple3{}, `{}`}, + {"empty value emitted", Marshaler{EmitDefaults: true}, &pb.Simple3{}, `{"dub":0}`}, {"map<int64, int32>", marshaler, &pb.Mappy{Nummy: map[int64]int32{1: 2, 3: 4}}, `{"nummy":{"1":2,"3":4}}`}, {"map<int64, int32>", marshalerAllOptions, &pb.Mappy{Nummy: map[int64]int32{1: 2, 3: 4}}, nummyPrettyJSON}, {"map<string, string>", marshaler,