Repository: zeromicro/go-zero
Pull request #2736 (2022-12-31 16:38:35)

fix: #2735


Patch:
diff --git a/core/mapping/jsonunmarshaler.go b/core/mapping/jsonunmarshaler.go
index 540581a3..f832224d 100644
--- a/core/mapping/jsonunmarshaler.go
+++ b/core/mapping/jsonunmarshaler.go
@@ -34,7 +34,7 @@ func getJsonUnmarshaler(opts ...UnmarshalOption) *Unmarshaler {
 }
 
 func unmarshalJsonBytes(content []byte, v interface{}, unmarshaler *Unmarshaler) error {
-	var m map[string]interface{}
+	var m interface{}
 	if err := jsonx.Unmarshal(content, &m); err != nil {
 		return err
 	}
@@ -43,7 +43,7 @@ func unmarshalJsonBytes(content []byte, v interface{}, unmarshaler *Unmarshaler)
 }
 
 func unmarshalJsonReader(reader io.Reader, v interface{}, unmarshaler *Unmarshaler) error {
-	var m map[string]interface{}
+	var m interface{}
 	if err := jsonx.UnmarshalFromReader(reader, &m); err != nil {
 		return err
 	}
diff --git a/core/mapping/unmarshaler.go b/core/mapping/unmarshaler.go
index 767b2903..e6c7425e 100644
--- a/core/mapping/unmarshaler.go
+++ b/core/mapping/unmarshaler.go
@@ -71,8 +71,29 @@ func UnmarshalKey(m map[string]interface{}, v interface{}) error {
 }
 
 // Unmarshal unmarshals m into v.
-func (u *Unmarshaler) Unmarshal(m map[string]interface{}, v interface{}) error {
-	return u.UnmarshalValuer(mapValuer(m), v)
+func (u *Unmarshaler) Unmarshal(i interface{}, v interface{}) error {
+	valueType := reflect.TypeOf(v)
+	if valueType.Kind() != reflect.Ptr {
+		return errValueNotSettable
+	}
+
+	elemType := valueType.Elem()
+	switch iv := i.(type) {
+	case map[string]interface{}:
+		if elemType.Kind() != reflect.Struct {
+			return errTypeMismatch
+		}
+
+		return u.UnmarshalValuer(mapValuer(iv), v)
+	case []interface{}:
+		if elemType.Kind() != reflect.Slice {
+			return errTypeMismatch
+		}
+
+		return u.fillSlice(elemType, reflect.ValueOf(v).Elem(), iv)
+	default:
+		return errUnsupportedType
+	}
 }
 
 // UnmarshalValuer unmarshals m into v.


Test patch:
diff --git a/core/mapping/jsonunmarshaler_test.go b/core/mapping/jsonunmarshaler_test.go
index 28615d31..05af99c9 100644
--- a/core/mapping/jsonunmarshaler_test.go
+++ b/core/mapping/jsonunmarshaler_test.go
@@ -856,8 +856,7 @@ func TestUnmarshalBytesError(t *testing.T) {
 	}
 
 	err := UnmarshalJsonBytes([]byte(payload), &v)
-	assert.NotNil(t, err)
-	assert.True(t, strings.Contains(err.Error(), payload))
+	assert.Equal(t, errTypeMismatch, err)
 }
 
 func TestUnmarshalReaderError(t *testing.T) {
@@ -867,9 +866,7 @@ func TestUnmarshalReaderError(t *testing.T) {
 		Any string
 	}
 
-	err := UnmarshalJsonReader(reader, &v)
-	assert.NotNil(t, err)
-	assert.True(t, strings.Contains(err.Error(), payload))
+	assert.Equal(t, errTypeMismatch, UnmarshalJsonReader(reader, &v))
 }
 
 func TestUnmarshalMap(t *testing.T) {
@@ -920,3 +917,16 @@ func TestUnmarshalMap(t *testing.T) {
 		assert.Equal(t, "foo", v.Any)
 	})
 }
+
+func TestUnmarshalJsonArray(t *testing.T) {
+	var v []struct {
+		Name string `json:"name"`
+		Age  int    `json:"age"`
+	}
+
+	body := `[{"name":"kevin", "age": 18}]`
+	assert.NoError(t, UnmarshalJsonBytes([]byte(body), &v))
+	assert.Equal(t, 1, len(v))
+	assert.Equal(t, "kevin", v[0].Name)
+	assert.Equal(t, 18, v[0].Age)
+}
diff --git a/core/mapping/unmarshaler_test.go b/core/mapping/unmarshaler_test.go
index c45b0600..8916e183 100644
--- a/core/mapping/unmarshaler_test.go
+++ b/core/mapping/unmarshaler_test.go
@@ -23,7 +23,14 @@ func TestUnmarshalWithFullNameNotStruct(t *testing.T) {
 	var s map[string]interface{}
 	content := []byte(`{"name":"xiaoming"}`)
 	err := UnmarshalJsonBytes(content, &s)
-	assert.Equal(t, errValueNotStruct, err)
+	assert.Equal(t, errTypeMismatch, err)
+}
+
+func TestUnmarshalValueNotSettable(t *testing.T) {
+	var s map[string]interface{}
+	content := []byte(`{"name":"xiaoming"}`)
+	err := UnmarshalJsonBytes(content, s)
+	assert.Equal(t, errValueNotSettable, err)
 }
 
 func TestUnmarshalWithoutTagName(t *testing.T) {
diff --git a/rest/httpx/requests_test.go b/rest/httpx/requests_test.go
index 70c35c1d..d6601f33 100644
--- a/rest/httpx/requests_test.go
+++ b/rest/httpx/requests_test.go
@@ -223,6 +223,22 @@ func TestParseJsonBody(t *testing.T) {
 		assert.Equal(t, "", v.Name)
 		assert.Equal(t, 0, v.Age)
 	})
+
+	t.Run("array body", func(t *testing.T) {
+		var v []struct {
+			Name string `json:"name"`
+			Age  int    `json:"age"`
+		}
+
+		body := `[{"name":"kevin", "age": 18}]`
+		r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body))
+		r.Header.Set(ContentType, header.JsonContentType)
+
+		assert.NoError(t, ParseJsonBody(r, &v))
+		assert.Equal(t, 1, len(v))
+		assert.Equal(t, "kevin", v[0].Name)
+		assert.Equal(t, 18, v[0].Age)
+	})
 }
 
 func TestParseRequired(t *testing.T) {