diff --git a/decode.go b/decode.go index 62d6654..a931856 100644 --- a/decode.go +++ b/decode.go @@ -2,6 +2,7 @@ package objconv import ( "encoding" + "encoding/json" "errors" "fmt" "reflect" @@ -946,6 +947,22 @@ func (d Decoder) decodeTextUnmarshaler(to reflect.Value) (t Type, err error) { return } +func (d Decoder) decodeJSONUnmarshaler(to reflect.Value) (t Type, err error) { + var s string + var v = reflect.ValueOf(&s).Elem() + + if t, err = d.decodeString(v); err != nil { + return + } + + if to.Kind() == reflect.Ptr && to.IsNil() { + to.Set(reflect.New(to.Type().Elem())) + } + + err = to.Interface().(json.Unmarshaler).UnmarshalJSON([]byte(s)) + return +} + func (d Decoder) decodeInterface(to reflect.Value) (t Type, err error) { if t, err = d.Parser.ParseType(); err == nil { err = d.decodeInterfaceFromType(t, to) @@ -1391,6 +1408,9 @@ func makeDecodeFunc(t reflect.Type, opts decodeFuncOpts) decodeFunc { case t.Implements(textUnmarshalerInterface): return Decoder.decodeTextUnmarshaler + + case t.Implements(jsonDecoderInterface): + return Decoder.decodeJSONUnmarshaler } switch p := reflect.PtrTo(t); { @@ -1405,6 +1425,9 @@ func makeDecodeFunc(t reflect.Type, opts decodeFuncOpts) decodeFunc { case p.Implements(textUnmarshalerInterface): return Decoder.decodeTextUnmarshalerPointer + + case p.Implements(jsonDecoderInterface): + return Decoder.decodeJSONUnmarshaler } // check what kind is the type, potentially generate a decoder diff --git a/decode_test.go b/decode_test.go index 1aa7f06..fef283a 100644 --- a/decode_test.go +++ b/decode_test.go @@ -9,6 +9,9 @@ import ( ) func TestDecoderDecodeType(t *testing.T) { + // var ex exampleJSONUnmarshaler + // json.Unmarshaler(&ex).UnmarshalJSON([]byte("2")) + date := time.Date(2016, 12, 12, 01, 01, 01, 0, time.UTC) err := errors.New("error") @@ -217,6 +220,9 @@ func TestDecoderDecodeType(t *testing.T) { // struct -> ptr {struct{ A int }{42}, &struct{ A int }{42}}, + + // bytes -> json unmarshaler + {"fake json", &exampleJSONUnmarshaler{[]byte("fake json")}}, } for _, test := range tests { @@ -402,3 +408,12 @@ func TestStreamRencode(t *testing.T) { }) } } + +type exampleJSONUnmarshaler struct { + b []byte +} + +func (ex *exampleJSONUnmarshaler) UnmarshalJSON(b []byte) error { + ex.b = b + return nil +} diff --git a/encode.go b/encode.go index 756e7f2..146be4c 100644 --- a/encode.go +++ b/encode.go @@ -2,6 +2,7 @@ package objconv import ( "encoding" + "encoding/json" "fmt" "io" "reflect" @@ -464,6 +465,14 @@ func (e Encoder) encodeTextMarshaler(v reflect.Value) error { return err } +func (e Encoder) encodeJSONMarshaler(v reflect.Value) error { + b, err := v.Interface().(json.Marshaler).MarshalJSON() + if err == nil { + err = e.Emitter.EmitString(stringNoCopy(b)) + } + return err +} + func (e Encoder) encodeUnsupported(v reflect.Value) error { return fmt.Errorf("objconv: the encoder doesn't support values of type %s", v.Type()) } @@ -764,6 +773,9 @@ func makeEncodeFunc(t reflect.Type, opts encodeFuncOpts) encodeFunc { case t.Implements(errorInterface): return Encoder.encodeError + + case t.Implements(jsonEncoderInterface): + return Encoder.encodeJSONMarshaler } switch t.Kind() { diff --git a/encode_test.go b/encode_test.go index a775280..684c131 100644 --- a/encode_test.go +++ b/encode_test.go @@ -26,6 +26,14 @@ type TFloat64 float64 type TString string type TBytes []byte +type exampleJSONMarshaler struct { + b []byte +} + +func (ex *exampleJSONMarshaler) MarshalJSON() ([]byte, error) { + return ex.b, nil +} + func TestEncoder(t *testing.T) { now := time.Now() err := errors.New("error") @@ -163,6 +171,9 @@ func TestEncoder(t *testing.T) { }, }, }, + + // json marshaler -> bytes + {&exampleJSONMarshaler{b: []byte("fake json")}, "fake json"}, } for _, test := range tests { diff --git a/value.go b/value.go index fe1c55c..b3157fa 100644 --- a/value.go +++ b/value.go @@ -2,6 +2,7 @@ package objconv import ( "encoding" + "encoding/json" "errors" "reflect" "sync" @@ -108,6 +109,8 @@ var ( // interfaces errorInterface = elemTypeOf((*error)(nil)) + jsonDecoderInterface = elemTypeOf((*json.Unmarshaler)(nil)) + jsonEncoderInterface = elemTypeOf((*json.Marshaler)(nil)) valueEncoderInterface = elemTypeOf((*ValueEncoder)(nil)) valueDecoderInterface = elemTypeOf((*ValueDecoder)(nil)) binaryMarshalerInterface = elemTypeOf((*encoding.BinaryMarshaler)(nil))