Explorar o código

goprotobuf: Improve and test JSON output of protocol buffers.

This doesn't completely fix issue 18 (we need the `json:"-"` support),
but the vast majority of messages don't carry unrecognized bytes, nor
use extensions, so this gets 99% of the way there.

Fixes #18.

R=r
CC=golang-dev
http://codereview.appspot.com/5167042
David Symonds %!s(int64=14) %!d(string=hai) anos
pai
achega
002ec408cf

+ 4 - 4
compiler/generator/generator.go

@@ -735,7 +735,6 @@ func (g *Generator) runPlugins(file *FileDescriptor) {
 	}
 }
 
-
 // FileOf return the FileDescriptor for this FileDescriptorProto.
 func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
 	for _, file := range g.allFiles {
@@ -1124,14 +1123,15 @@ func (g *Generator) generateMessage(message *Descriptor) {
 		fieldname := CamelCase(*field.Name)
 		typename, wiretype := g.GoType(message, field)
 		jsonName := *field.Name
-		tag := fmt.Sprintf("`protobuf:%s json:%q`", g.goTag(field, wiretype), jsonName)
+		tag := fmt.Sprintf("`protobuf:%s json:%q`", g.goTag(field, wiretype), jsonName+",omitempty")
 		g.P(fieldname, "\t", typename, "\t", tag)
 		g.RecordTypeUse(proto.GetString(field.TypeName))
 	}
+	// TODO: Use `json:"-"` for these XXX_ fields when that makes it into a Go release.
 	if len(message.ExtensionRange) > 0 {
-		g.P("XXX_extensions\t\tmap[int32]", g.ProtoPkg, ".Extension")
+		g.P("XXX_extensions\t\tmap[int32]", g.ProtoPkg, ".Extension `json:\",omitempty\"`")
 	}
-	g.P("XXX_unrecognized\t[]byte")
+	g.P("XXX_unrecognized\t[]byte `json:\",omitempty\"`")
 	g.Out()
 	g.P("}")
 

+ 5 - 5
compiler/testdata/imp.pb.go.golden

@@ -48,9 +48,9 @@ func (x ImportedMessage_Owner) String() string {
 }
 
 type ImportedMessage struct {
-	Field            *int64 `protobuf:"varint,1,req,name=field" json:"field"`
-	XXX_extensions   map[int32][]byte
-	XXX_unrecognized []byte
+	Field            *int64           `protobuf:"varint,1,req,name=field" json:"field,omitempty"`
+	XXX_extensions   map[int32][]byte `json:",omitempty"`
+	XXX_unrecognized []byte           `json:",omitempty"`
 }
 
 func (this *ImportedMessage) Reset()         { *this = ImportedMessage{} }
@@ -71,8 +71,8 @@ func (this *ImportedMessage) ExtensionMap() map[int32][]byte {
 }
 
 type ImportedExtendable struct {
-	XXX_extensions   map[int32][]byte
-	XXX_unrecognized []byte
+	XXX_extensions   map[int32][]byte `json:",omitempty"`
+	XXX_unrecognized []byte           `json:",omitempty"`
 }
 
 func (this *ImportedExtendable) Reset()         { *this = ImportedExtendable{} }

+ 21 - 21
compiler/testdata/test.pb.go.golden

@@ -117,14 +117,14 @@ func (x Reply_Entry_Game) String() string {
 }
 
 type Request struct {
-	Key              []int64                     `protobuf:"varint,1,rep,name=key" json:"key"`
-	ImportedMessage  *imp1.ImportedMessage       `protobuf:"bytes,2,opt,name=imported_message" json:"imported_message"`
-	Hue              *Request_Color              `protobuf:"varint,3,opt,name=hue,enum=my.test.Request_Color" json:"hue"`
-	Hat              *HatType                    `protobuf:"varint,4,opt,name=hat,enum=my.test.HatType,def=1" json:"hat"`
-	Owner            *imp1.ImportedMessage_Owner `protobuf:"varint,6,opt,name=owner,enum=imp.ImportedMessage_Owner" json:"owner"`
-	Deadline         *float32                    `protobuf:"fixed32,7,opt,name=deadline,def=inf" json:"deadline"`
-	Somegroup        *Request_SomeGroup          `protobuf:"group,8,opt,name=SomeGroup" json:"somegroup"`
-	XXX_unrecognized []byte
+	Key              []int64                     `protobuf:"varint,1,rep,name=key" json:"key,omitempty"`
+	ImportedMessage  *imp1.ImportedMessage       `protobuf:"bytes,2,opt,name=imported_message" json:"imported_message,omitempty"`
+	Hue              *Request_Color              `protobuf:"varint,3,opt,name=hue,enum=my.test.Request_Color" json:"hue,omitempty"`
+	Hat              *HatType                    `protobuf:"varint,4,opt,name=hat,enum=my.test.HatType,def=1" json:"hat,omitempty"`
+	Owner            *imp1.ImportedMessage_Owner `protobuf:"varint,6,opt,name=owner,enum=imp.ImportedMessage_Owner" json:"owner,omitempty"`
+	Deadline         *float32                    `protobuf:"fixed32,7,opt,name=deadline,def=inf" json:"deadline,omitempty"`
+	Somegroup        *Request_SomeGroup          `protobuf:"group,8,opt,name=SomeGroup" json:"somegroup,omitempty"`
+	XXX_unrecognized []byte                      `json:",omitempty"`
 }
 
 func (this *Request) Reset()         { *this = Request{} }
@@ -135,18 +135,18 @@ const Default_Request_Hat HatType = HatType_FEDORA
 var Default_Request_Deadline float32 = float32(math.Inf(1))
 
 type Request_SomeGroup struct {
-	GroupField       *int32 `protobuf:"varint,9,opt,name=group_field" json:"group_field"`
-	XXX_unrecognized []byte
+	GroupField       *int32 `protobuf:"varint,9,opt,name=group_field" json:"group_field,omitempty"`
+	XXX_unrecognized []byte `json:",omitempty"`
 }
 
 func (this *Request_SomeGroup) Reset()         { *this = Request_SomeGroup{} }
 func (this *Request_SomeGroup) String() string { return proto.CompactTextString(this) }
 
 type Reply struct {
-	Found            []*Reply_Entry `protobuf:"bytes,1,rep,name=found" json:"found"`
-	CompactKeys      []int32        `protobuf:"varint,2,rep,packed,name=compact_keys" json:"compact_keys"`
-	XXX_extensions   map[int32]proto.Extension
-	XXX_unrecognized []byte
+	Found            []*Reply_Entry            `protobuf:"bytes,1,rep,name=found" json:"found,omitempty"`
+	CompactKeys      []int32                   `protobuf:"varint,2,rep,packed,name=compact_keys" json:"compact_keys,omitempty"`
+	XXX_extensions   map[int32]proto.Extension `json:",omitempty"`
+	XXX_unrecognized []byte                    `json:",omitempty"`
 }
 
 func (this *Reply) Reset()         { *this = Reply{} }
@@ -167,10 +167,10 @@ func (this *Reply) ExtensionMap() map[int32]proto.Extension {
 }
 
 type Reply_Entry struct {
-	KeyThatNeeds_1234Camel_CasIng *int64 `protobuf:"varint,1,req,name=key_that_needs_1234camel_CasIng" json:"key_that_needs_1234camel_CasIng"`
-	Value                         *int64 `protobuf:"varint,2,opt,name=value,def=7" json:"value"`
-	XMyFieldName_2                *int64 `protobuf:"varint,3,opt,name=_my_field_name_2" json:"_my_field_name_2"`
-	XXX_unrecognized              []byte
+	KeyThatNeeds_1234Camel_CasIng *int64 `protobuf:"varint,1,req,name=key_that_needs_1234camel_CasIng" json:"key_that_needs_1234camel_CasIng,omitempty"`
+	Value                         *int64 `protobuf:"varint,2,opt,name=value,def=7" json:"value,omitempty"`
+	XMyFieldName_2                *int64 `protobuf:"varint,3,opt,name=_my_field_name_2" json:"_my_field_name_2,omitempty"`
+	XXX_unrecognized              []byte `json:",omitempty"`
 }
 
 func (this *Reply_Entry) Reset()         { *this = Reply_Entry{} }
@@ -179,7 +179,7 @@ func (this *Reply_Entry) String() string { return proto.CompactTextString(this)
 const Default_Reply_Entry_Value int64 = 7
 
 type ReplyExtensions struct {
-	XXX_unrecognized []byte
+	XXX_unrecognized []byte `json:",omitempty"`
 }
 
 func (this *ReplyExtensions) Reset()         { *this = ReplyExtensions{} }
@@ -194,8 +194,8 @@ var E_ReplyExtensions_Time = &proto.ExtensionDesc{
 }
 
 type OldReply struct {
-	XXX_extensions   map[int32]proto.Extension
-	XXX_unrecognized []byte
+	XXX_extensions   map[int32]proto.Extension `json:",omitempty"`
+	XXX_unrecognized []byte                    `json:",omitempty"`
 }
 
 func (this *OldReply) Reset()         { *this = OldReply{} }

+ 21 - 0
proto/all_test.go

@@ -35,6 +35,7 @@ package proto_test
 import (
 	"bytes"
 	"fmt"
+	"json"
 	"math"
 	"os"
 	"reflect"
@@ -1301,6 +1302,26 @@ func TestMaximumTagNumber(t *testing.T) {
 	}
 }
 
+func TestJSON(t *testing.T) {
+	m := &MyMessage{
+		Count: Int32(4),
+		Pet:   []string{"bunny", "kitty"},
+		Inner: &InnerMessage{
+			Host: String("cauchy"),
+		},
+	}
+	const expected = `{"count":4,"pet":["bunny","kitty"],"inner":{"host":"cauchy"}}`
+
+	b, err := json.Marshal(m)
+	if err != nil {
+		t.Fatalf("json.Marshal failed: %v", err)
+	}
+	s := string(b)
+	if s != expected {
+		t.Errorf("got  %s\nwant %s", s, expected)
+	}
+}
+
 func BenchmarkMarshal(b *testing.B) {
 	b.StopTimer()