diff --git a/internal/decoder/jitdec/compiler.go b/internal/decoder/jitdec/compiler.go index 2a3180f81..2ad3f6d82 100644 --- a/internal/decoder/jitdec/compiler.go +++ b/internal/decoder/jitdec/compiler.go @@ -563,6 +563,11 @@ func (self *_Compiler) checkMarshaler(p *_Program, vt reflect.Type, flags int, e return true } + if flags == checkMarshalerFlags_quoted { + // text marshaler shouldn't be supported for quoted string + return false + } + /* check for `encoding.TextMarshaler` with pointer receiver */ if pt.Implements(encodingTextUnmarshalerType) { if exec { @@ -972,7 +977,7 @@ func (self *_Compiler) compileStructFieldStrUnmarshal(p *_Program, vt reflect.Ty } func (self *_Compiler) compileStructFieldStr(p *_Program, sp int, vt reflect.Type) { - // according to std, Unmarshaler should be called before stringize + // according to std, json.Unmarshaler should be called before stringize // see https://github.com/bytedance/sonic/issues/670 if self.checkMarshaler(p, vt, checkMarshalerFlags_quoted, false) { self.compileStructFieldStrUnmarshal(p, vt) diff --git a/issue_test/issue670_test.go b/issue_test/issue670_test.go index 0497a33e1..f4605ab8a 100644 --- a/issue_test/issue670_test.go +++ b/issue_test/issue670_test.go @@ -17,6 +17,7 @@ package issue_test import ( "encoding/json" "fmt" + "reflect" "testing" "time" @@ -24,43 +25,69 @@ import ( "github.com/stretchr/testify/assert" ) -func TestIssue670_encode(t *testing.T) { - var obj = Issue670Case{ D: Date(time.Now().Unix()) } +func TestIssue670_JSONMarshaler(t *testing.T) { + var obj = Issue670JSONMarshaler{ D: Date(time.Now().Unix()) } so, _ := sonic.MarshalString(obj) eo, _ := json.Marshal(obj) assert.Equal(t, string(eo), so) println(string(eo)) } -func TestIssue670_decode(t *testing.T) { +func TestIssue670_JSONUnmarshaler(t *testing.T) { // match eo := []byte(`{"D":"2021-08-26","E":1}`) - testUnmarshal(t, eo) + et := reflect.TypeOf(Issue670JSONMarshaler{}) + testUnmarshal(t, eo, et, true) // mismatch eo = []byte(`{"D":11,"E":1}`) - testUnmarshal(t, eo) + testUnmarshal(t, eo, et, true) // null eo = []byte(`{"D":null,"E":1}`) - testUnmarshal(t, eo) + testUnmarshal(t, eo, et, true) } -func testUnmarshal(t *testing.T, eo []byte) { - obj := Issue670Case{} +func testUnmarshal(t *testing.T, eo []byte, rt reflect.Type, checkobj bool) { + obj := reflect.New(rt).Interface() println(string(eo)) println("sonic") - es := sonic.Unmarshal(eo, &obj) - obj2 := Issue670Case{} + es := sonic.Unmarshal(eo, obj) + obj2 := reflect.New(rt).Interface() println("std") - ee := json.Unmarshal(eo, &obj2) + ee := json.Unmarshal(eo, obj2) assert.Equal(t, ee ==nil, es == nil, es) - assert.Equal(t, obj2, obj) + if checkobj { + assert.Equal(t, obj2, obj) + } fmt.Printf("std: %v, obj: %#v", ee, obj2) fmt.Printf("sonic error: %v, obj: %#v", es, obj) } -type Issue670Case struct { +func TestIssue670_TextMarshaler(t *testing.T) { + var obj = Issue670TextMarshaler{ D: int(time.Now().Unix()) } + so, _ := sonic.MarshalString(obj) + eo, _ := json.Marshal(obj) + assert.Equal(t, string(eo), so) + println(string(eo)) +} + +func TestIssue670_TextUnmarshaler(t *testing.T) { + // match + eo := []byte(`{"D":"2021-08-26","E":1}`) + et := reflect.TypeOf(Issue670TextMarshaler{}) + testUnmarshal(t, eo, et, false) + + // mismatch + eo = []byte(`{"D":11,"E":1}`) + testUnmarshal(t, eo, et, false) + + // null + eo = []byte(`{"D":null,"E":1}`) + testUnmarshal(t, eo, et, true) +} + +type Issue670JSONMarshaler struct { D Date `form:"D" json:"D,string" query:"D"` E int } @@ -79,6 +106,7 @@ func (d *Date) UnmarshalJSON(in []byte) error { *d = 0 return nil } + println("hook ", string(in)) t, err := time.Parse("2006-01-02", string(in)) if err != nil { @@ -87,3 +115,33 @@ func (d *Date) UnmarshalJSON(in []byte) error { *d = Date(t.Unix()) return nil } + +type Issue670TextMarshaler struct { + D int `form:"D" json:"D,string" query:"D"` + E int +} + + +type Date2 int64 + +func (d Date2) MarshalText() ([]byte, error) { + println("hook 1") + if d == 0 { + return []byte("null"), nil + } + return []byte(fmt.Sprintf("\"%s\"", time.Unix(int64(d), 0).Format("2006-01-02"))), nil +} + +func (d *Date2) UnmarshalText(in []byte) error { + println("hook 2", string(in)) + if string(in) == "null" { + *d = 0 + return nil + } + t, err := time.Parse("2006-01-02", string(in)) + if err != nil { + return err + } + *d = Date2(t.Unix()) + return nil +} \ No newline at end of file