From 0b176e17aca8a379256cb2a8d626b7d574659c4c Mon Sep 17 00:00:00 2001 From: Kevin Wan Date: Wed, 11 Jan 2023 00:45:11 +0800 Subject: [PATCH] fix: #2576 (#2776) --- core/mapping/unmarshaler.go | 43 ++++++++++++++++++++++---------- core/mapping/unmarshaler_test.go | 28 +++++++++++++++++++++ 2 files changed, 58 insertions(+), 13 deletions(-) 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, 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"`