package json import ( "fmt" "math/big" "reflect" "github.com/aws/smithy-go/document" "github.com/aws/smithy-go/document/internal/serde" smithyjson "github.com/aws/smithy-go/encoding/json" ) // EncoderOptions is the set of options that can be configured for an Encoder. type EncoderOptions struct{} // Encoder is a Smithy document decoder for JSON based protocols. type Encoder struct { options EncoderOptions } // Encode returns the JSON encoding of v. func (e *Encoder) Encode(v interface{}) ([]byte, error) { encoder := smithyjson.NewEncoder() if err := e.encode(jsonValueProvider(encoder.Value), reflect.ValueOf(v), serde.Tag{}); err != nil { return nil, err } encodedBytes := encoder.Bytes() if len(encodedBytes) == 0 { return nil, nil } return encodedBytes, nil } // valueProvider is an interface for retrieving a JSON Value type used for encoding. type valueProvider interface { GetValue() smithyjson.Value } // jsonValueProvider is a valueProvider that returns the JSON value encoder as is. type jsonValueProvider smithyjson.Value func (p jsonValueProvider) GetValue() smithyjson.Value { return smithyjson.Value(p) } // jsonObjectKeyProvider is a valueProvider that returns a JSON value type for encoding a value for the given JSON object // key. type jsonObjectKeyProvider struct { Object *smithyjson.Object Key string } func (p jsonObjectKeyProvider) GetValue() smithyjson.Value { return p.Object.Key(p.Key) } // jsonArrayProvider is a valueProvider that returns a JSON value type for encoding a value in the given JSON array. type jsonArrayProvider struct { Array *smithyjson.Array } func (p jsonArrayProvider) GetValue() smithyjson.Value { return p.Array.Value() } func (e *Encoder) encode(vp valueProvider, rv reflect.Value, tag serde.Tag) error { // Zero values are serialized as null, or skipped if omitEmpty. if serde.IsZeroValue(rv) { if tag.OmitEmpty { return nil } return e.encodeZeroValue(vp, rv) } // Handle both pointers and interface conversion into types rv = serde.ValueElem(rv) switch rv.Kind() { case reflect.Invalid: if tag.OmitEmpty { return nil } vp.GetValue().Null() return nil case reflect.Struct: return e.encodeStruct(vp, rv) case reflect.Map: return e.encodeMap(vp, rv) case reflect.Slice, reflect.Array: return e.encodeSlice(vp, rv) case reflect.Chan, reflect.Func, reflect.UnsafePointer: // skip unsupported types return nil default: return e.encodeScalar(vp, rv) } } func (e *Encoder) encodeZeroValue(vp valueProvider, rv reflect.Value) error { switch rv.Kind() { case reflect.Invalid: vp.GetValue().Null() case reflect.Array: vp.GetValue().Array().Close() case reflect.Map, reflect.Slice: vp.GetValue().Null() case reflect.String: vp.GetValue().String("") case reflect.Bool: vp.GetValue().Boolean(false) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: vp.GetValue().Long(0) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: vp.GetValue().ULong(0) case reflect.Float32, reflect.Float64: vp.GetValue().Double(0) case reflect.Interface, reflect.Ptr: vp.GetValue().Null() default: return &document.InvalidMarshalError{Message: fmt.Sprintf("unknown value type: %s", rv.String())} } return nil } func (e *Encoder) encodeStruct(vp valueProvider, rv reflect.Value) error { if rv.CanInterface() && document.IsNoSerde(rv.Interface()) { return &document.UnmarshalTypeError{ Value: fmt.Sprintf("unsupported type"), Type: rv.Type(), } } switch { case rv.Type().ConvertibleTo(serde.ReflectTypeOf.Time): return &document.InvalidMarshalError{ Message: fmt.Sprintf("unsupported type %s", rv.Type().String()), } case rv.Type().ConvertibleTo(serde.ReflectTypeOf.BigFloat): fallthrough case rv.Type().ConvertibleTo(serde.ReflectTypeOf.BigInt): return e.encodeNumber(vp, rv) } object := vp.GetValue().Object() defer object.Close() fields := serde.GetStructFields(rv.Type()) for _, f := range fields.All() { if f.Name == "" { return &document.InvalidMarshalError{Message: "map key cannot be empty"} } fv, found := serde.EncoderFieldByIndex(rv, f.Index) if !found { continue } err := e.encode(jsonObjectKeyProvider{ Object: object, Key: f.Name, }, fv, f.Tag) if err != nil { return err } } return nil } func (e *Encoder) encodeMap(vp valueProvider, rv reflect.Value) error { object := vp.GetValue().Object() defer object.Close() for _, key := range rv.MapKeys() { keyName := fmt.Sprint(key.Interface()) if keyName == "" { return &document.InvalidMarshalError{Message: "map key cannot be empty"} } ev := rv.MapIndex(key) err := e.encode(jsonObjectKeyProvider{ Object: object, Key: keyName, }, ev, serde.Tag{}) if err != nil { return err } } return nil } func (e *Encoder) encodeSlice(value valueProvider, rv reflect.Value) error { array := value.GetValue().Array() defer array.Close() for i := 0; i < rv.Len(); i++ { err := e.encode(jsonArrayProvider{Array: array}, rv.Index(i), serde.Tag{}) if err != nil { return err } } return nil } func (e *Encoder) encodeScalar(vp valueProvider, rv reflect.Value) error { if rv.Type() == serde.ReflectTypeOf.DocumentNumber { number := rv.Interface().(document.Number) if !isValidJSONNumber(number.String()) { return &document.InvalidMarshalError{Message: fmt.Sprintf("invalid number literal: %s", number)} } vp.GetValue().Write([]byte(number)) } switch rv.Kind() { case reflect.Bool: vp.GetValue().Boolean(rv.Bool()) case reflect.String: vp.GetValue().String(rv.String()) default: // Fallback to encoding numbers, will return invalid type if not supported err := e.encodeNumber(vp, rv) if err != nil { return err } } return nil } func (e *Encoder) encodeNumber(vp valueProvider, rv reflect.Value) error { switch rv.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: vp.GetValue().Long(rv.Int()) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: vp.GetValue().ULong(rv.Uint()) case reflect.Float32: vp.GetValue().Float(float32(rv.Float())) case reflect.Float64: vp.GetValue().Double(rv.Float()) default: rvt := rv.Type() switch { case rvt.ConvertibleTo(serde.ReflectTypeOf.BigInt): bi := rv.Convert(serde.ReflectTypeOf.BigInt).Interface().(big.Int) vp.GetValue().BigInteger(&bi) case rvt.ConvertibleTo(serde.ReflectTypeOf.BigFloat): bf := rv.Convert(serde.ReflectTypeOf.BigFloat).Interface().(big.Float) vp.GetValue().BigDecimal(&bf) default: return &document.InvalidMarshalError{Message: fmt.Sprintf("incompatible type: %s", rvt.String())} } } return nil } // isValidJSONNumber reports whether s is a valid JSON number literal. // From https://golang.org/src/encoding/json/encode.go#L652 isValidNumber // Copyright 2010 The Go Authors. func isValidJSONNumber(s string) bool { // This function implements the JSON numbers grammar. // See https://tools.ietf.org/html/rfc7159#section-6 // and https://www.json.org/img/number.png if s == "" { return false } // Optional - if s[0] == '-' { s = s[1:] if s == "" { return false } } // Digits switch { default: return false case s[0] == '0': s = s[1:] case '1' <= s[0] && s[0] <= '9': s = s[1:] for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { s = s[1:] } } // . followed by 1 or more digits. if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' { s = s[2:] for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { s = s[1:] } } // e or E followed by an optional - or + and // 1 or more digits. if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') { s = s[1:] if s[0] == '+' || s[0] == '-' { s = s[1:] if s == "" { return false } } for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { s = s[1:] } } // Make sure we are at the end. return s == "" }