package jsoniter import ( "encoding" "encoding/json" "unsafe" "github.com/modern-go/reflect2" ) var marshalerType = reflect2.TypeOfPtr((*json.Marshaler)(nil)).Elem() var unmarshalerType = reflect2.TypeOfPtr((*json.Unmarshaler)(nil)).Elem() var textMarshalerType = reflect2.TypeOfPtr((*encoding.TextMarshaler)(nil)).Elem() var textUnmarshalerType = reflect2.TypeOfPtr((*encoding.TextUnmarshaler)(nil)).Elem() func createDecoderOfMarshaler(ctx *ctx, typ reflect2.Type) ValDecoder { ptrType := reflect2.PtrTo(typ) if ptrType.Implements(unmarshalerType) { return &referenceDecoder{ &unmarshalerDecoder{ptrType}, } } if ptrType.Implements(textUnmarshalerType) { return &referenceDecoder{ &textUnmarshalerDecoder{ptrType}, } } return nil } func createEncoderOfMarshaler(ctx *ctx, typ reflect2.Type) ValEncoder { if typ == marshalerType { checkIsEmpty := createCheckIsEmpty(ctx, typ) var encoder ValEncoder = &directMarshalerEncoder{ checkIsEmpty: checkIsEmpty, } return encoder } if typ.Implements(marshalerType) { checkIsEmpty := createCheckIsEmpty(ctx, typ) var encoder ValEncoder = &marshalerEncoder{ valType: typ, checkIsEmpty: checkIsEmpty, } return encoder } ptrType := reflect2.PtrTo(typ) if ctx.prefix != "" && ptrType.Implements(marshalerType) { checkIsEmpty := createCheckIsEmpty(ctx, ptrType) var encoder ValEncoder = &marshalerEncoder{ valType: ptrType, checkIsEmpty: checkIsEmpty, } return &referenceEncoder{encoder} } if typ == textMarshalerType { checkIsEmpty := createCheckIsEmpty(ctx, typ) var encoder ValEncoder = &directTextMarshalerEncoder{ checkIsEmpty: checkIsEmpty, stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), } return encoder } if typ.Implements(textMarshalerType) { checkIsEmpty := createCheckIsEmpty(ctx, typ) var encoder ValEncoder = &textMarshalerEncoder{ valType: typ, stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), checkIsEmpty: checkIsEmpty, } return encoder } // if prefix is empty, the type is the root type if ctx.prefix != "" && ptrType.Implements(textMarshalerType) { checkIsEmpty := createCheckIsEmpty(ctx, ptrType) var encoder ValEncoder = &textMarshalerEncoder{ valType: ptrType, stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), checkIsEmpty: checkIsEmpty, } return &referenceEncoder{encoder} } return nil } type marshalerEncoder struct { checkIsEmpty checkIsEmpty valType reflect2.Type } func (encoder *marshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { obj := encoder.valType.UnsafeIndirect(ptr) if encoder.valType.IsNullable() && reflect2.IsNil(obj) { stream.WriteNil() return } marshaler := obj.(json.Marshaler) bytes, err := marshaler.MarshalJSON() if err != nil { stream.Error = err } else { // html escape was already done by jsoniter // but the extra '\n' should be trimed l := len(bytes) if l > 0 && bytes[l-1] == '\n' { bytes = bytes[:l-1] } stream.Write(bytes) } } func (encoder *marshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool { return encoder.checkIsEmpty.IsEmpty(ptr) } type directMarshalerEncoder struct { checkIsEmpty checkIsEmpty } func (encoder *directMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { marshaler := *(*json.Marshaler)(ptr) if marshaler == nil { stream.WriteNil() return } bytes, err := marshaler.MarshalJSON() if err != nil { stream.Error = err } else { stream.Write(bytes) } } func (encoder *directMarshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool { return encoder.checkIsEmpty.IsEmpty(ptr) } type textMarshalerEncoder struct { valType reflect2.Type stringEncoder ValEncoder checkIsEmpty checkIsEmpty } func (encoder *textMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { obj := encoder.valType.UnsafeIndirect(ptr) if encoder.valType.IsNullable() && reflect2.IsNil(obj) { stream.WriteNil() return } marshaler := (obj).(encoding.TextMarshaler) bytes, err := marshaler.MarshalText() if err != nil { stream.Error = err } else { str := string(bytes) encoder.stringEncoder.Encode(unsafe.Pointer(&str), stream) } } func (encoder *textMarshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool { return encoder.checkIsEmpty.IsEmpty(ptr) } type directTextMarshalerEncoder struct { stringEncoder ValEncoder checkIsEmpty checkIsEmpty } func (encoder *directTextMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { marshaler := *(*encoding.TextMarshaler)(ptr) if marshaler == nil { stream.WriteNil() return } bytes, err := marshaler.MarshalText() if err != nil { stream.Error = err } else { str := string(bytes) encoder.stringEncoder.Encode(unsafe.Pointer(&str), stream) } } func (encoder *directTextMarshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool { return encoder.checkIsEmpty.IsEmpty(ptr) } type unmarshalerDecoder struct { valType reflect2.Type } func (decoder *unmarshalerDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { valType := decoder.valType obj := valType.UnsafeIndirect(ptr) unmarshaler := obj.(json.Unmarshaler) iter.nextToken() iter.unreadByte() // skip spaces bytes := iter.SkipAndReturnBytes() err := unmarshaler.UnmarshalJSON(bytes) if err != nil { iter.ReportError("unmarshalerDecoder", err.Error()) } } type textUnmarshalerDecoder struct { valType reflect2.Type } func (decoder *textUnmarshalerDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { valType := decoder.valType obj := valType.UnsafeIndirect(ptr) if reflect2.IsNil(obj) { ptrType := valType.(*reflect2.UnsafePtrType) elemType := ptrType.Elem() elem := elemType.UnsafeNew() ptrType.UnsafeSet(ptr, unsafe.Pointer(&elem)) obj = valType.UnsafeIndirect(ptr) } unmarshaler := (obj).(encoding.TextUnmarshaler) str := iter.ReadString() err := unmarshaler.UnmarshalText([]byte(str)) if err != nil { iter.ReportError("textUnmarshalerDecoder", err.Error()) } }