Expose the thorny part of the oneof metadata interpretation.
This makes it very easy for other code to understand what to do with
incoming data that references oneof fields.
diff --git a/jsonpb/jsonpb.go b/jsonpb/jsonpb.go
index 2663c92..389c5e0 100644
--- a/jsonpb/jsonpb.go
+++ b/jsonpb/jsonpb.go
@@ -332,41 +332,15 @@
}
}
// Check for any oneof fields.
- // This might be slow; we can optimise it if it becomes a problem.
- type oneofMessage interface {
- XXX_OneofFuncs() (func(proto.Message, *proto.Buffer) error, func(proto.Message, int, int, *proto.Buffer) (bool, error), []interface{})
- }
- var oneofTypes []interface{}
- if om, ok := reflect.Zero(reflect.PtrTo(targetType)).Interface().(oneofMessage); ok {
- _, _, oneofTypes = om.XXX_OneofFuncs()
- }
+ sprops := proto.GetProperties(targetType)
for fname, raw := range jsonFields {
- for _, oot := range oneofTypes {
- sp := reflect.ValueOf(oot).Type() // *T
- var props proto.Properties
- props.Parse(sp.Elem().Field(0).Tag.Get("protobuf"))
- if props.OrigName != fname {
- continue
- }
- nv := reflect.New(sp.Elem())
- // There will be exactly one interface field that
- // this new value is assignable to.
- for i := 0; i < targetType.NumField(); i++ {
- f := targetType.Field(i)
- if f.Type.Kind() != reflect.Interface {
- continue
- }
- if !nv.Type().AssignableTo(f.Type) {
- continue
- }
- target.Field(i).Set(nv)
- break
- }
+ if oop, ok := sprops.OneofTypes[fname]; ok {
+ 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)
- break
}
}
if len(jsonFields) > 0 {
diff --git a/proto/properties.go b/proto/properties.go
index 5685445..692fe43 100644
--- a/proto/properties.go
+++ b/proto/properties.go
@@ -142,7 +142,17 @@
oneofMarshaler oneofMarshaler
oneofUnmarshaler oneofUnmarshaler
stype reflect.Type
- oneofTypes []interface{}
+
+ // OneofTypes contains information about the oneof fields in this message.
+ // It is keyed by the original name of a field.
+ OneofTypes map[string]*OneofProperties
+}
+
+// OneofProperties represents information about a specific field in a oneof.
+type OneofProperties struct {
+ Type reflect.Type // pointer to generated struct type for this oneof field
+ Field int // struct field number of the containing oneof in the message
+ Prop *Properties
}
// Implement the sorting interface so we can sort the fields in tag order, as recommended by the spec.
@@ -698,8 +708,35 @@
XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), []interface{})
}
if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok {
- prop.oneofMarshaler, prop.oneofUnmarshaler, prop.oneofTypes = om.XXX_OneofFuncs()
+ var oots []interface{}
+ prop.oneofMarshaler, prop.oneofUnmarshaler, oots = om.XXX_OneofFuncs()
prop.stype = t
+
+ // Interpret oneof metadata.
+ prop.OneofTypes = make(map[string]*OneofProperties)
+ for _, oot := range oots {
+ oop := &OneofProperties{
+ Type: reflect.ValueOf(oot).Type(), // *T
+ Prop: new(Properties),
+ }
+ sft := oop.Type.Elem().Field(0)
+ oop.Prop.Name = sft.Name
+ oop.Prop.Parse(sft.Tag.Get("protobuf"))
+ // There will be exactly one interface field that
+ // this new value is assignable to.
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ if f.Type.Kind() != reflect.Interface {
+ continue
+ }
+ if !oop.Type.AssignableTo(f.Type) {
+ continue
+ }
+ oop.Field = i
+ break
+ }
+ prop.OneofTypes[oop.Prop.OrigName] = oop
+ }
}
// build required counts
diff --git a/proto/text_parser.go b/proto/text_parser.go
index 2e2a67f..8bce369 100644
--- a/proto/text_parser.go
+++ b/proto/text_parser.go
@@ -532,34 +532,12 @@
fi, props, ok := structFieldByName(sprops, name)
if ok {
dst = sv.Field(fi)
- } else {
- // Maybe it is a oneof.
- // TODO: If this shows in profiles, cache the mapping.
- for _, oot := range sprops.oneofTypes {
- sp := reflect.ValueOf(oot).Type() // *T
- var p Properties
- p.Parse(sp.Elem().Field(0).Tag.Get("protobuf"))
- if p.OrigName != name {
- continue
- }
- nv := reflect.New(sp.Elem())
- dst = nv.Elem().Field(0)
- props = &p
- // There will be exactly one interface field that
- // this new value is assignable to.
- for i := 0; i < st.NumField(); i++ {
- f := st.Field(i)
- if f.Type.Kind() != reflect.Interface {
- continue
- }
- if !nv.Type().AssignableTo(f.Type) {
- continue
- }
- sv.Field(i).Set(nv)
- break
- }
- break
- }
+ } else if oop, ok := sprops.OneofTypes[name]; ok {
+ // It is a oneof.
+ props = oop.Prop
+ nv := reflect.New(oop.Type.Elem())
+ dst = nv.Elem().Field(0)
+ sv.Field(oop.Field).Set(nv)
}
if !dst.IsValid() {
return p.errorf("unknown field name %q in %v", name, st)