// Empty returns a new proto.Message of the type specified in a // google.protobuf.Any message. It returns an error if corresponding message // type isn't linked in. func EmptyAny(any *Any) (proto.Message, error) { aname, err := AnyMessageName(any) if err != nil { return nil, err } t := proto.MessageType(aname) if t == nil { return nil, fmt.Errorf("any: message type %q isn't linked in", aname) } return reflect.New(t.Elem()).Interface().(proto.Message), nil }
func (m *Marshaler) marshalAny(out *errWriter, any proto.Message, indent string) error { // "If the Any contains a value that has a special JSON mapping, // it will be converted as follows: {"@type": xxx, "value": yyy}. // Otherwise, the value will be converted into a JSON object, // and the "@type" field will be inserted to indicate the actual data type." v := reflect.ValueOf(any).Elem() turl := v.Field(0).String() val := v.Field(1).Bytes() // Only the part of type_url after the last slash is relevant. mname := turl if slash := strings.LastIndex(mname, "/"); slash >= 0 { mname = mname[slash+1:] } mt := proto.MessageType(mname) if mt == nil { return fmt.Errorf("unknown message type %q", mname) } msg := reflect.New(mt.Elem()).Interface().(proto.Message) if err := proto.Unmarshal(val, msg); err != nil { return err } if _, ok := msg.(isWkt); ok { out.write("{") if m.Indent != "" { out.write("\n") } if err := m.marshalTypeURL(out, indent, turl); err != nil { return err } m.writeSep(out) if m.Indent != "" { out.write(indent) out.write(m.Indent) out.write(`"value": `) } else { out.write(`"value":`) } if err := m.marshalObject(out, msg, indent+m.Indent, ""); err != nil { return err } if m.Indent != "" { out.write("\n") out.write(indent) } out.write("}") return out.err } return m.marshalObject(out, msg, indent, turl) }
// marshalValue writes the value to the Writer. func (m *Marshaler) marshalValue(out *errWriter, prop *proto.Properties, v reflect.Value, indent string) error { v = reflect.Indirect(v) // Handle repeated elements. if v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8 { out.write("[") comma := "" for i := 0; i < v.Len(); i++ { sliceVal := v.Index(i) out.write(comma) if m.Indent != "" { out.write("\n") out.write(indent) out.write(m.Indent) out.write(m.Indent) } if err := m.marshalValue(out, prop, sliceVal, indent+m.Indent); err != nil { return err } comma = "," } if m.Indent != "" { out.write("\n") out.write(indent) out.write(m.Indent) } out.write("]") return out.err } // Handle well-known types. // Most are handled up in marshalObject (because 99% are messages). if wkt, ok := v.Interface().(isWkt); ok { switch wkt.XXX_WellKnownType() { case "NullValue": out.write("null") return out.err } } if t, ok := v.Interface().(time.Time); ok { ts, err := types.TimestampProto(t) if err != nil { return err } return m.marshalValue(out, prop, reflect.ValueOf(ts), indent) } if d, ok := v.Interface().(time.Duration); ok { dur := types.DurationProto(d) return m.marshalValue(out, prop, reflect.ValueOf(dur), indent) } // Handle enumerations. if !m.EnumsAsInts && prop.Enum != "" { // Unknown enum values will are stringified by the proto library as their // value. Such values should _not_ be quoted or they will be interpreted // as an enum string instead of their value. enumStr := v.Interface().(fmt.Stringer).String() var valStr string if v.Kind() == reflect.Ptr { valStr = strconv.Itoa(int(v.Elem().Int())) } else { valStr = strconv.Itoa(int(v.Int())) } if m, ok := v.Interface().(interface { MarshalJSON() ([]byte, error) }); ok { data, err := m.MarshalJSON() if err != nil { return err } enumStr = string(data) enumStr, err = strconv.Unquote(enumStr) if err != nil { return err } } isKnownEnum := enumStr != valStr if isKnownEnum { out.write(`"`) } out.write(enumStr) if isKnownEnum { out.write(`"`) } return out.err } // Handle nested messages. if v.Kind() == reflect.Struct { i := v if v.CanAddr() { i = v.Addr() } else { i = reflect.New(v.Type()) i.Elem().Set(v) } iface := i.Interface() if iface == nil { out.write(`null`) return out.err } pm, ok := iface.(proto.Message) if !ok { if prop.CustomType == "" { return fmt.Errorf("%v does not implement proto.Message", v.Type()) } t := proto.MessageType(prop.CustomType) if t == nil || !i.Type().ConvertibleTo(t) { return fmt.Errorf("%v declared custom type %s but it is not convertible to %v", v.Type(), prop.CustomType, t) } pm = i.Convert(t).Interface().(proto.Message) } return m.marshalObject(out, pm, indent+m.Indent, "") } // Handle maps. // Since Go randomizes map iteration, we sort keys for stable output. if v.Kind() == reflect.Map { out.write(`{`) keys := v.MapKeys() sort.Sort(mapKeys(keys)) for i, k := range keys { if i > 0 { out.write(`,`) } if m.Indent != "" { out.write("\n") out.write(indent) out.write(m.Indent) out.write(m.Indent) } b, err := json.Marshal(k.Interface()) if err != nil { return err } s := string(b) // If the JSON is not a string value, encode it again to make it one. if !strings.HasPrefix(s, `"`) { b, err := json.Marshal(s) if err != nil { return err } s = string(b) } out.write(s) out.write(`:`) if m.Indent != "" { out.write(` `) } if err := m.marshalValue(out, prop, v.MapIndex(k), indent+m.Indent); err != nil { return err } } if m.Indent != "" { out.write("\n") out.write(indent) out.write(m.Indent) } out.write(`}`) return out.err } // Default handling defers to the encoding/json library. b, err := json.Marshal(v.Interface()) if err != nil { return err } needToQuote := string(b[0]) != `"` && (v.Kind() == reflect.Int64 || v.Kind() == reflect.Uint64) if needToQuote { out.write(`"`) } out.write(string(b)) if needToQuote { out.write(`"`) } return out.err }