package json import ( "bytes" "encoding/json" "errors" "fmt" "io" "reflect" "strconv" "time" ) var ( timeType = reflect.TypeOf(time.Time{}) jsonMarshalerType = reflect.TypeOf(new(json.Marshaler)).Elem() jsonUnmarshalerType = reflect.TypeOf(new(json.Unmarshaler)).Elem() ) // Marshal marshals the value as JSON, using Amino-compatible JSON encoding (strings for // 64-bit numbers, and type wrappers for registered types). func Marshal(v interface{}) ([]byte, error) { buf := new(bytes.Buffer) err := encode(buf, v) if err != nil { return nil, err } return buf.Bytes(), nil } // MarshalIndent marshals the value as JSON, using the given prefix and indentation. func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { bz, err := Marshal(v) if err != nil { return nil, err } buf := new(bytes.Buffer) err = json.Indent(buf, bz, prefix, indent) if err != nil { return nil, err } return buf.Bytes(), nil } func encode(w io.Writer, v interface{}) error { // Bare nil values can't be reflected, so we must handle them here. if v == nil { return writeStr(w, "null") } rv := reflect.ValueOf(v) // If this is a registered type, defer to interface encoder regardless of whether the input is // an interface or a bare value. This retains Amino's behavior, but is inconsistent with // behavior in structs where an interface field will get the type wrapper while a bare value // field will not. if typeRegistry.name(rv.Type()) != "" { return encodeReflectInterface(w, rv) } return encodeReflect(w, rv) } func encodeReflect(w io.Writer, rv reflect.Value) error { if !rv.IsValid() { return errors.New("invalid reflect value") } // Recursively dereference if pointer. for rv.Kind() == reflect.Ptr { if rv.IsNil() { return writeStr(w, "null") } rv = rv.Elem() } // Convert times to UTC. if rv.Type() == timeType { rv = reflect.ValueOf(rv.Interface().(time.Time).Round(0).UTC()) } // If the value implements json.Marshaler, defer to stdlib directly. Since we've already // dereferenced, we try implementations with both value receiver and pointer receiver. We must // do this after the time normalization above, and thus after dereferencing. if rv.Type().Implements(jsonMarshalerType) { return encodeStdlib(w, rv.Interface()) } else if rv.CanAddr() && rv.Addr().Type().Implements(jsonMarshalerType) { return encodeStdlib(w, rv.Addr().Interface()) } switch rv.Type().Kind() { // Complex types must be recursively encoded. case reflect.Interface: return encodeReflectInterface(w, rv) case reflect.Array, reflect.Slice: return encodeReflectList(w, rv) case reflect.Map: return encodeReflectMap(w, rv) case reflect.Struct: return encodeReflectStruct(w, rv) // 64-bit integers are emitted as strings, to avoid precision problems with e.g. // Javascript which uses 64-bit floats (having 53-bit precision). case reflect.Int64, reflect.Int: return writeStr(w, `"`+strconv.FormatInt(rv.Int(), 10)+`"`) case reflect.Uint64, reflect.Uint: return writeStr(w, `"`+strconv.FormatUint(rv.Uint(), 10)+`"`) // For everything else, defer to the stdlib encoding/json encoder default: return encodeStdlib(w, rv.Interface()) } } func encodeReflectList(w io.Writer, rv reflect.Value) error { // Emit nil slices as null. if rv.Kind() == reflect.Slice && rv.IsNil() { return writeStr(w, "null") } // Encode byte slices as base64 with the stdlib encoder. if rv.Type().Elem().Kind() == reflect.Uint8 { // Stdlib does not base64-encode byte arrays, only slices, so we copy to slice. if rv.Type().Kind() == reflect.Array { slice := reflect.MakeSlice(reflect.SliceOf(rv.Type().Elem()), rv.Len(), rv.Len()) reflect.Copy(slice, rv) rv = slice } return encodeStdlib(w, rv.Interface()) } // Anything else we recursively encode ourselves. length := rv.Len() if err := writeStr(w, "["); err != nil { return err } for i := 0; i < length; i++ { if err := encodeReflect(w, rv.Index(i)); err != nil { return err } if i < length-1 { if err := writeStr(w, ","); err != nil { return err } } } return writeStr(w, "]") } func encodeReflectMap(w io.Writer, rv reflect.Value) error { if rv.Type().Key().Kind() != reflect.String { return errors.New("map key must be string") } // nil maps are not emitted as nil, to retain Amino compatibility. if err := writeStr(w, "{"); err != nil { return err } writeComma := false for _, keyrv := range rv.MapKeys() { if writeComma { if err := writeStr(w, ","); err != nil { return err } } if err := encodeStdlib(w, keyrv.Interface()); err != nil { return err } if err := writeStr(w, ":"); err != nil { return err } if err := encodeReflect(w, rv.MapIndex(keyrv)); err != nil { return err } writeComma = true } return writeStr(w, "}") } func encodeReflectStruct(w io.Writer, rv reflect.Value) error { sInfo := makeStructInfo(rv.Type()) if err := writeStr(w, "{"); err != nil { return err } writeComma := false for i, fInfo := range sInfo.fields { frv := rv.Field(i) if fInfo.hidden || (fInfo.omitEmpty && frv.IsZero()) { continue } if writeComma { if err := writeStr(w, ","); err != nil { return err } } if err := encodeStdlib(w, fInfo.jsonName); err != nil { return err } if err := writeStr(w, ":"); err != nil { return err } if err := encodeReflect(w, frv); err != nil { return err } writeComma = true } return writeStr(w, "}") } func encodeReflectInterface(w io.Writer, rv reflect.Value) error { // Get concrete value and dereference pointers. for rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface { if rv.IsNil() { return writeStr(w, "null") } rv = rv.Elem() } // Look up the name of the concrete type name := typeRegistry.name(rv.Type()) if name == "" { return fmt.Errorf("cannot encode unregistered type %v", rv.Type()) } // Write value wrapped in interface envelope if err := writeStr(w, fmt.Sprintf(`{"type":%q,"value":`, name)); err != nil { return err } if err := encodeReflect(w, rv); err != nil { return err } return writeStr(w, "}") } func encodeStdlib(w io.Writer, v interface{}) error { // Doesn't stream the output because that adds a newline, as per: // https://golang.org/pkg/encoding/json/#Encoder.Encode blob, err := json.Marshal(v) if err != nil { return err } _, err = w.Write(blob) return err } func writeStr(w io.Writer, s string) error { _, err := w.Write([]byte(s)) return err }