From f3b8fef34f0dab72b4884c28541a11c14e47f8a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=B6=E7=A6=8F?= <30451714+cjf8134@users.noreply.github.com> Date: Fri, 1 Jul 2022 23:21:31 +0800 Subject: [PATCH] fix: type matching supports string to int (#2038) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: type matching supports string to int * feat: type matching supports string to int Co-authored-by: 程家福 --- core/mapping/unmarshaler.go | 4 ++- core/mapping/unmarshaler_test.go | 52 ++++++++++++++++++++++++++- core/mapping/utils.go | 15 ++++++++ core/mapping/utils_test.go | 47 +++++++++++++++++++++++- rest/internal/encoding/parser_test.go | 11 ++++++ 5 files changed, 126 insertions(+), 3 deletions(-) diff --git a/core/mapping/unmarshaler.go b/core/mapping/unmarshaler.go index 57e05979ad96..497a9db8130c 100644 --- a/core/mapping/unmarshaler.go +++ b/core/mapping/unmarshaler.go @@ -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. diff --git a/core/mapping/unmarshaler_test.go b/core/mapping/unmarshaler_test.go index 3c113ed13e61..2efed6259655 100644 --- a/core/mapping/unmarshaler_test.go +++ b/core/mapping/unmarshaler_test.go @@ -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"` @@ -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 { @@ -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 { diff --git a/core/mapping/utils.go b/core/mapping/utils.go index fee4e99cf85b..a793ecbe139a 100644 --- a/core/mapping/utils.go +++ b/core/mapping/utils.go @@ -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 { @@ -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 { diff --git a/core/mapping/utils_test.go b/core/mapping/utils_test.go index fd5194cd4c37..eac00beb3192 100644 --- a/core/mapping/utils_test.go +++ b/core/mapping/utils_test.go @@ -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 { @@ -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() diff --git a/rest/internal/encoding/parser_test.go b/rest/internal/encoding/parser_test.go index 70e001f47bec..b04b3f72cfae 100644 --- a/rest/internal/encoding/parser_test.go +++ b/rest/internal/encoding/parser_test.go @@ -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) +} \ No newline at end of file