Repository: zeromicro/go-zero
Pull request #2776 (2023-01-10 16:11:05)

fix: #2576


Patch:
diff --git a/core/mapping/unmarshaler.go b/core/mapping/unmarshaler.go
index e6c7425e..b7285057 100644
--- a/core/mapping/unmarshaler.go
+++ b/core/mapping/unmarshaler.go
@@ -731,24 +731,41 @@ func (u *Unmarshaler) processNamedFieldWithValue(fieldType reflect.Type, value r
 		return u.processFieldNotFromString(fieldType, value, vp, opts, fullName)
 	default:
 		if u.opts.fromString || opts.fromString() {
-			valueKind := reflect.TypeOf(mapValue).Kind()
-			if valueKind != reflect.String {
-				return fmt.Errorf("error: the value in map is not string, but %s", valueKind)
-			}
+			return u.processNamedFieldWithValueFromString(fieldType, value, mapValue,
+				key, opts, fullName)
+		}
 
-			options := opts.options()
-			if len(options) > 0 {
-				if !stringx.Contains(options, mapValue.(string)) {
-					return fmt.Errorf(`error: value "%s" for field "%s" is not defined in options "%v"`,
-						mapValue, key, options)
-				}
-			}
+		return u.processFieldNotFromString(fieldType, value, vp, opts, fullName)
+	}
+}
+
+func (u *Unmarshaler) processNamedFieldWithValueFromString(fieldType reflect.Type, value reflect.Value,
+	mapValue interface{}, key string, opts *fieldOptionsWithContext, fullName string) error {
+	valueKind := reflect.TypeOf(mapValue).Kind()
+	if valueKind != reflect.String {
+		return fmt.Errorf("the value in map is not string, but %s", valueKind)
+	}
 
-			return fillPrimitive(fieldType, value, mapValue, opts, fullName)
+	options := opts.options()
+	if len(options) > 0 {
+		var checkValue string
+		switch mt := mapValue.(type) {
+		case string:
+			checkValue = mt
+		case json.Number:
+			checkValue = mt.String()
+		default:
+			return fmt.Errorf("the value in map is not string or json.Number, but %s",
+				valueKind.String())
 		}
 
-		return u.processFieldNotFromString(fieldType, value, vp, opts, fullName)
+		if !stringx.Contains(options, checkValue) {
+			return fmt.Errorf(`value "%s" for field "%s" is not defined in options "%v"`,
+				mapValue, key, options)
+		}
 	}
+
+	return fillPrimitive(fieldType, value, mapValue, opts, fullName)
 }
 
 func (u *Unmarshaler) processNamedFieldWithoutValue(fieldType reflect.Type, value reflect.Value,


Test patch:
diff --git a/core/mapping/unmarshaler_test.go b/core/mapping/unmarshaler_test.go
index 8916e183..83229fd4 100644
--- a/core/mapping/unmarshaler_test.go
+++ b/core/mapping/unmarshaler_test.go
@@ -250,6 +250,34 @@ func TestUnmarshalIntWithDefault(t *testing.T) {
 	assert.Equal(t, 1, in.Int)
 }
 
+func TestUnmarshalIntWithString(t *testing.T) {
+	t.Run("int without options", func(t *testing.T) {
+		type inner struct {
+			Int int64 `key:"int,string"`
+		}
+		m := map[string]interface{}{
+			"int": json.Number("0"),
+		}
+
+		var in inner
+		assert.Nil(t, UnmarshalKey(m, &in))
+		assert.Equal(t, int64(0), in.Int)
+	})
+
+	t.Run("int with options", func(t *testing.T) {
+		type inner struct {
+			Int int64 `key:"int,string,options=[0,1]"`
+		}
+		m := map[string]interface{}{
+			"int": json.Number("0"),
+		}
+
+		var in inner
+		assert.Nil(t, UnmarshalKey(m, &in))
+		assert.Equal(t, int64(0), in.Int)
+	})
+}
+
 func TestUnmarshalBoolSliceRequired(t *testing.T) {
 	type inner struct {
 		Bools []bool `key:"bools"`