Bläddra i källkod

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.
David Symonds 10 år sedan
förälder
incheckning
1baed096cd
3 ändrade filer med 49 tillägg och 60 borttagningar
  1. 4 30
      jsonpb/jsonpb.go
  2. 39 2
      proto/properties.go
  3. 6 28
      proto/text_parser.go

+ 4 - 30
jsonpb/jsonpb.go

@@ -332,41 +332,15 @@ func unmarshalValue(target reflect.Value, inputValue json.RawMessage) error {
 			}
 		}
 		// 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 {

+ 39 - 2
proto/properties.go

@@ -142,7 +142,17 @@ type StructProperties struct {
 	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 @@ func getPropertiesLocked(t reflect.Type) *StructProperties {
 		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

+ 6 - 28
proto/text_parser.go

@@ -532,34 +532,12 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
 		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)