Skip to content

Commit 7fdceb6

Browse files
committed
jsonpb: Adds support for other []byte types to be base64 encoded.
The proto3 spec encodes the bytes type to be a base64 encoded string when marshalling to jsonpb. This adds support to jsonpb to correctly encode go types in the format `type Bytes []byte` into base64 instead of being represented as an uint8 array. While these types won't be generated by the standard protobuf compiler, they can be generated by others such as gogo protobuf.
1 parent 9e6977f commit 7fdceb6

File tree

3 files changed

+20
-7
lines changed

3 files changed

+20
-7
lines changed

jsonpb/jsonpb.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,6 @@ import (
5353
"github.com/golang/protobuf/proto"
5454
)
5555

56-
var (
57-
byteArrayType = reflect.TypeOf([]byte{})
58-
)
59-
6056
// Marshaler is a configurable object for converting between
6157
// protocol buffer objects and a JSON representation for them.
6258
type Marshaler struct {
@@ -374,7 +370,7 @@ func (m *Marshaler) marshalValue(out *errWriter, prop *proto.Properties, v refle
374370
v = reflect.Indirect(v)
375371

376372
// Handle repeated elements.
377-
if v.Type() != byteArrayType && v.Kind() == reflect.Slice {
373+
if v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8 {
378374
out.write("[")
379375
comma := ""
380376
for i := 0; i < v.Len(); i++ {
@@ -684,7 +680,7 @@ func unmarshalValue(target reflect.Value, inputValue json.RawMessage, prop *prot
684680
}
685681

686682
// Handle arrays (which aren't encoded bytes)
687-
if targetType != byteArrayType && targetType.Kind() == reflect.Slice {
683+
if targetType.Kind() == reflect.Slice && targetType.Elem().Kind() != reflect.Uint8 {
688684
var slc []json.RawMessage
689685
if err := json.Unmarshal(inputValue, &slc); err != nil {
690686
return err

jsonpb/jsonpb_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,21 @@ func init() {
282282
}
283283
}
284284

285+
// Bytes is used to test that []byte type aliases are serialized to base64.
286+
type Byte byte
287+
288+
// Bytes is used to test that []byte type aliases are serialized to base64.
289+
type Bytes []Byte
290+
291+
// BytesMessage is used to test that []byte type aliases are serialized to base64.
292+
type BytesMessage struct {
293+
Bytes Bytes `protobuf:"bytes,1,opt,name=bytes,json=bytes" json:"bytes,omitempty"`
294+
}
295+
296+
func (*BytesMessage) Reset() {}
297+
func (*BytesMessage) String() string { return "" }
298+
func (*BytesMessage) ProtoMessage() {}
299+
285300
var marshalingTests = []struct {
286301
desc string
287302
marshaler Marshaler
@@ -373,6 +388,7 @@ var marshalingTests = []struct {
373388
{"BoolValue", marshaler, &pb.KnownTypes{Bool: &wpb.BoolValue{Value: true}}, `{"bool":true}`},
374389
{"StringValue", marshaler, &pb.KnownTypes{Str: &wpb.StringValue{Value: "plush"}}, `{"str":"plush"}`},
375390
{"BytesValue", marshaler, &pb.KnownTypes{Bytes: &wpb.BytesValue{Value: []byte("wow")}}, `{"bytes":"d293"}`},
391+
{"BytesMessageValue", marshaler, &BytesMessage{Bytes: []Byte("wow")}, `{"bytes":"d293"}`},
376392
}
377393

378394
func TestMarshaling(t *testing.T) {
@@ -445,6 +461,7 @@ var unmarshalingTests = []struct {
445461
{"BoolValue", `{"bool":true}`, &pb.KnownTypes{Bool: &wpb.BoolValue{Value: true}}},
446462
{"StringValue", `{"str":"plush"}`, &pb.KnownTypes{Str: &wpb.StringValue{Value: "plush"}}},
447463
{"BytesValue", `{"bytes":"d293"}`, &pb.KnownTypes{Bytes: &wpb.BytesValue{Value: []byte("wow")}}},
464+
{"BytesMessageValue", `{"bytes":"d293"}`, &BytesMessage{Bytes: []Byte("wow")}},
448465
// `null` is also a permissible value. Let's just test one.
449466
{"null DoubleValue", `{"dbl":null}`, &pb.KnownTypes{Dbl: &wpb.DoubleValue{}}},
450467
}

proto/text.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Propert
513513
switch v.Kind() {
514514
case reflect.Slice:
515515
// Should only be a []byte; repeated fields are handled in writeStruct.
516-
if err := writeString(w, string(v.Interface().([]byte))); err != nil {
516+
if err := writeString(w, string(v.Bytes())); err != nil {
517517
return err
518518
}
519519
case reflect.String:

0 commit comments

Comments
 (0)