Skip to content

Commit

Permalink
fix: type matching supports string to int (#2038)
Browse files Browse the repository at this point in the history
* fix: type matching supports string to int

* feat: type matching supports string to int

Co-authored-by: 程家福 <chengjiafu@uniontech.com>
  • Loading branch information
cjf8134 and 程家福 committed Jul 1, 2022
1 parent 6a4885b commit f3b8fef
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 3 deletions.
4 changes: 3 additions & 1 deletion core/mapping/unmarshaler.go
Expand Up @@ -534,8 +534,10 @@ func (u *Unmarshaler) fillSliceValue(slice reflect.Value, index int,
baseKind reflect.Kind, value interface{}) error {
ithVal := slice.Index(index)
switch v := value.(type) {
case json.Number:
case fmt.Stringer:
return setValue(baseKind, ithVal, v.String())
case string:
return setValue(baseKind, ithVal, v)
default:
// don't need to consider the difference between int, int8, int16, int32, int64,
// uint, uint8, uint16, uint32, uint64, because they're handled as json.Number.
Expand Down
52 changes: 51 additions & 1 deletion core/mapping/unmarshaler_test.go
Expand Up @@ -2681,7 +2681,7 @@ func TestUnmarshalJsonReaderMultiArray(t *testing.T) {
assert.Equal(t, 2, len(res.B))
}

func TestUnmarshalJsonReaderPtrMultiArray(t *testing.T) {
func TestUnmarshalJsonReaderPtrMultiArrayString(t *testing.T) {
payload := `{"a": "133", "b": [["add", "cccd"], ["eeee"]]}`
var res struct {
A string `json:"a"`
Expand All @@ -2694,6 +2694,32 @@ func TestUnmarshalJsonReaderPtrMultiArray(t *testing.T) {
assert.Equal(t, 2, len(res.B[0]))
}

func TestUnmarshalJsonReaderPtrMultiArrayString_Int(t *testing.T) {
payload := `{"a": "133", "b": [[11, 22], [33]]}`
var res struct {
A string `json:"a"`
B [][]*string `json:"b"`
}
reader := strings.NewReader(payload)
err := UnmarshalJsonReader(reader, &res)
assert.Nil(t, err)
assert.Equal(t, 2, len(res.B))
assert.Equal(t, 2, len(res.B[0]))
}

func TestUnmarshalJsonReaderPtrMultiArrayInt(t *testing.T) {
payload := `{"a": "133", "b": [[11, 22], [33]]}`
var res struct {
A string `json:"a"`
B [][]*int `json:"b"`
}
reader := strings.NewReader(payload)
err := UnmarshalJsonReader(reader, &res)
assert.Nil(t, err)
assert.Equal(t, 2, len(res.B))
assert.Equal(t, 2, len(res.B[0]))
}

func TestUnmarshalJsonReaderPtrArray(t *testing.T) {
payload := `{"a": "133", "b": ["add", "cccd", "eeee"]}`
var res struct {
Expand All @@ -2706,6 +2732,30 @@ func TestUnmarshalJsonReaderPtrArray(t *testing.T) {
assert.Equal(t, 3, len(res.B))
}

func TestUnmarshalJsonReaderPtrArray_Int(t *testing.T) {
payload := `{"a": "133", "b": [11, 22, 33]}`
var res struct {
A string `json:"a"`
B []*string `json:"b"`
}
reader := strings.NewReader(payload)
err := UnmarshalJsonReader(reader, &res)
assert.Nil(t, err)
assert.Equal(t, 3, len(res.B))
}

func TestUnmarshalJsonReaderPtrInt(t *testing.T) {
payload := `{"a": "133", "b": [11, 22, 33]}`
var res struct {
A string `json:"a"`
B []*string `json:"b"`
}
reader := strings.NewReader(payload)
err := UnmarshalJsonReader(reader, &res)
assert.Nil(t, err)
assert.Equal(t, 3, len(res.B))
}

func TestUnmarshalJsonWithoutKey(t *testing.T) {
payload := `{"A": "1", "B": "2"}`
var res struct {
Expand Down
15 changes: 15 additions & 0 deletions core/mapping/utils.go
Expand Up @@ -60,6 +60,20 @@ func Deref(t reflect.Type) reflect.Type {
return t
}

// DerefVal dereferences a value, if pointer value nil set new a value, returns is not a ptr element value.
func DerefVal(v reflect.Value) reflect.Value {
for {
if v.Kind() != reflect.Ptr {
break
}
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}
v = v.Elem()
}
return v
}

// Repr returns the string representation of v.
func Repr(v interface{}) string {
if v == nil {
Expand Down Expand Up @@ -477,6 +491,7 @@ func setValue(kind reflect.Kind, value reflect.Value, str string) error {
if !value.CanSet() {
return errValueNotSettable
}
value = DerefVal(value)

v, err := convertType(kind, str)
if err != nil {
Expand Down
47 changes: 46 additions & 1 deletion core/mapping/utils_test.go
Expand Up @@ -15,7 +15,7 @@ type Foo struct {
StrWithTagAndOption string `key:"stringwithtag,string"`
}

func TestDeferInt(t *testing.T) {
func TestDerefInt(t *testing.T) {
i := 1
s := "hello"
number := struct {
Expand Down Expand Up @@ -60,6 +60,51 @@ func TestDeferInt(t *testing.T) {
}
}

func TestDerefValInt(t *testing.T) {
i := 1
s := "hello"
number := struct {
f float64
}{
f: 6.4,
}
cases := []struct {
t reflect.Value
expect reflect.Kind
}{
{
t: reflect.ValueOf(i),
expect: reflect.Int,
},
{
t: reflect.ValueOf(&i),
expect: reflect.Int,
},
{
t: reflect.ValueOf(s),
expect: reflect.String,
},
{
t: reflect.ValueOf(&s),
expect: reflect.String,
},
{
t: reflect.ValueOf(number.f),
expect: reflect.Float64,
},
{
t: reflect.ValueOf(&number.f),
expect: reflect.Float64,
},
}

for _, each := range cases {
t.Run(each.t.String(), func(t *testing.T) {
assert.Equal(t, each.expect, DerefVal(each.t).Kind())
})
}
}

func TestParseKeyAndOptionWithoutTag(t *testing.T) {
var foo Foo
rte := reflect.TypeOf(&foo).Elem()
Expand Down
11 changes: 11 additions & 0 deletions rest/internal/encoding/parser_test.go
Expand Up @@ -38,3 +38,14 @@ func TestParseHeadersMulti(t *testing.T) {
assert.Equal(t, 1, val.Baz)
assert.True(t, val.Qux)
}

func TestParseHeadersArrayInt(t *testing.T) {
var val struct {
Foo []int `header:"foo"`
}
r := httptest.NewRequest(http.MethodGet, "/any", nil)
r.Header.Set("foo", "1")
r.Header.Add("foo", "2")
assert.Nil(t, ParseHeaders(r.Header, &val))
assert.Equal(t, []int{1, 2}, val.Foo)
}

0 comments on commit f3b8fef

Please sign in to comment.