소스 검색

fix: type matching supports string to int (#2038)

* fix: type matching supports string to int

* feat: type matching supports string to int

Co-authored-by: 程家福 <chengjiafu@uniontech.com>
家福 2 년 전
부모
커밋
f3b8fef34f
5개의 변경된 파일126개의 추가작업 그리고 3개의 파일을 삭제
  1. 3 1
      core/mapping/unmarshaler.go
  2. 51 1
      core/mapping/unmarshaler_test.go
  3. 15 0
      core/mapping/utils.go
  4. 46 1
      core/mapping/utils_test.go
  5. 11 0
      rest/internal/encoding/parser_test.go

+ 3 - 1
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.

+ 51 - 1
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 {

+ 15 - 0
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 {

+ 46 - 1
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()

+ 11 - 0
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)
+}