Skip to content

Commit

Permalink
feat: text.Marshaler shouldn't support string tag
Browse files Browse the repository at this point in the history
  • Loading branch information
AsterDY committed Aug 6, 2024
1 parent a8b77eb commit 8959c82
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 14 deletions.
7 changes: 6 additions & 1 deletion internal/decoder/jitdec/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down
84 changes: 71 additions & 13 deletions issue_test/issue670_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,50 +17,77 @@ package issue_test
import (
"encoding/json"
"fmt"
"reflect"
"testing"
"time"

"github.com/bytedance/sonic"
"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
}
Expand All @@ -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 {
Expand All @@ -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
}

0 comments on commit 8959c82

Please sign in to comment.