fix: camel cased key of map item in config (#2715)
* fix: camel cased key of map item in config * fix: mapping anonymous problem * fix: mapping anonymous problem * chore: refactor * chore: add more tests * chore: refactor
This commit is contained in:
@@ -376,19 +376,51 @@ func (u *Unmarshaler) processAnonymousField(field reflect.StructField, value ref
|
||||
return err
|
||||
}
|
||||
|
||||
if _, hasValue := getValue(m, key); hasValue {
|
||||
return fmt.Errorf("fields of %s can't be wrapped inside, because it's anonymous", key)
|
||||
}
|
||||
|
||||
if options.optional() {
|
||||
return u.processAnonymousFieldOptional(field.Type, value, key, m, fullName)
|
||||
return u.processAnonymousFieldOptional(field, value, key, m, fullName)
|
||||
}
|
||||
|
||||
return u.processAnonymousFieldRequired(field.Type, value, m, fullName)
|
||||
return u.processAnonymousFieldRequired(field, value, m, fullName)
|
||||
}
|
||||
|
||||
func (u *Unmarshaler) processAnonymousFieldOptional(fieldType reflect.Type, value reflect.Value,
|
||||
func (u *Unmarshaler) processAnonymousFieldOptional(field reflect.StructField, value reflect.Value,
|
||||
key string, m valuerWithParent, fullName string) error {
|
||||
derefedFieldType := Deref(field.Type)
|
||||
|
||||
switch derefedFieldType.Kind() {
|
||||
case reflect.Struct:
|
||||
return u.processAnonymousStructFieldOptional(field.Type, value, key, m, fullName)
|
||||
default:
|
||||
return u.processNamedField(field, value, m, fullName)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Unmarshaler) processAnonymousFieldRequired(field reflect.StructField, value reflect.Value,
|
||||
m valuerWithParent, fullName string) error {
|
||||
fieldType := field.Type
|
||||
maybeNewValue(fieldType, value)
|
||||
derefedFieldType := Deref(fieldType)
|
||||
indirectValue := reflect.Indirect(value)
|
||||
|
||||
switch derefedFieldType.Kind() {
|
||||
case reflect.Struct:
|
||||
for i := 0; i < derefedFieldType.NumField(); i++ {
|
||||
if err := u.processField(derefedFieldType.Field(i), indirectValue.Field(i),
|
||||
m, fullName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
if err := u.processNamedField(field, indirectValue, m, fullName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Unmarshaler) processAnonymousStructFieldOptional(fieldType reflect.Type,
|
||||
value reflect.Value, key string, m valuerWithParent, fullName string) error {
|
||||
var filled bool
|
||||
var required int
|
||||
var requiredFilled int
|
||||
@@ -428,21 +460,6 @@ func (u *Unmarshaler) processAnonymousFieldOptional(fieldType reflect.Type, valu
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Unmarshaler) processAnonymousFieldRequired(fieldType reflect.Type, value reflect.Value,
|
||||
m valuerWithParent, fullName string) error {
|
||||
maybeNewValue(fieldType, value)
|
||||
derefedFieldType := Deref(fieldType)
|
||||
indirectValue := reflect.Indirect(value)
|
||||
|
||||
for i := 0; i < derefedFieldType.NumField(); i++ {
|
||||
if err := u.processField(derefedFieldType.Field(i), indirectValue.Field(i), m, fullName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Unmarshaler) processField(field reflect.StructField, value reflect.Value,
|
||||
m valuerWithParent, fullName string) error {
|
||||
if usingDifferentKeys(u.key, field) {
|
||||
|
||||
@@ -212,6 +212,24 @@ func TestUnmarshalIntPtr(t *testing.T) {
|
||||
assert.Equal(t, 1, *in.Int)
|
||||
}
|
||||
|
||||
func TestUnmarshalIntSliceOfPtr(t *testing.T) {
|
||||
type inner struct {
|
||||
Ints []*int `key:"ints"`
|
||||
}
|
||||
m := map[string]interface{}{
|
||||
"ints": []int{1, 2, 3},
|
||||
}
|
||||
|
||||
var in inner
|
||||
assert.NoError(t, UnmarshalKey(m, &in))
|
||||
assert.NotEmpty(t, in.Ints)
|
||||
var ints []int
|
||||
for _, i := range in.Ints {
|
||||
ints = append(ints, *i)
|
||||
}
|
||||
assert.EqualValues(t, []int{1, 2, 3}, ints)
|
||||
}
|
||||
|
||||
func TestUnmarshalIntWithDefault(t *testing.T) {
|
||||
type inner struct {
|
||||
Int int `key:"int,default=5"`
|
||||
@@ -3665,6 +3683,7 @@ func TestUnmarshalJsonBytesSliceOfMaps(t *testing.T) {
|
||||
Name string `json:"name"`
|
||||
ActualAmount int `json:"actual_amount"`
|
||||
}
|
||||
|
||||
OrderApplyRefundReq struct {
|
||||
OrderId string `json:"order_id"`
|
||||
RefundReason RefundReasonData `json:"refund_reason,optional"`
|
||||
@@ -3676,6 +3695,130 @@ func TestUnmarshalJsonBytesSliceOfMaps(t *testing.T) {
|
||||
assert.NoError(t, UnmarshalJsonBytes(input, &req))
|
||||
}
|
||||
|
||||
func TestUnmarshalJsonBytesWithAnonymousField(t *testing.T) {
|
||||
type (
|
||||
Int int
|
||||
|
||||
InnerConf struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
Conf struct {
|
||||
Int
|
||||
InnerConf
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
input = []byte(`{"Name": "hello", "Int": 3}`)
|
||||
c Conf
|
||||
)
|
||||
assert.NoError(t, UnmarshalJsonBytes(input, &c))
|
||||
assert.Equal(t, "hello", c.Name)
|
||||
assert.Equal(t, Int(3), c.Int)
|
||||
}
|
||||
|
||||
func TestUnmarshalJsonBytesWithAnonymousFieldOptional(t *testing.T) {
|
||||
type (
|
||||
Int int
|
||||
|
||||
InnerConf struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
Conf struct {
|
||||
Int `json:",optional"`
|
||||
InnerConf
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
input = []byte(`{"Name": "hello", "Int": 3}`)
|
||||
c Conf
|
||||
)
|
||||
assert.NoError(t, UnmarshalJsonBytes(input, &c))
|
||||
assert.Equal(t, "hello", c.Name)
|
||||
assert.Equal(t, Int(3), c.Int)
|
||||
}
|
||||
|
||||
func TestUnmarshalJsonBytesWithAnonymousFieldBadTag(t *testing.T) {
|
||||
type (
|
||||
Int int
|
||||
|
||||
InnerConf struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
Conf struct {
|
||||
Int `json:",optional=123"`
|
||||
InnerConf
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
input = []byte(`{"Name": "hello", "Int": 3}`)
|
||||
c Conf
|
||||
)
|
||||
assert.Error(t, UnmarshalJsonBytes(input, &c))
|
||||
}
|
||||
|
||||
func TestUnmarshalJsonBytesWithAnonymousFieldBadValue(t *testing.T) {
|
||||
type (
|
||||
Int int
|
||||
|
||||
InnerConf struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
Conf struct {
|
||||
Int
|
||||
InnerConf
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
input = []byte(`{"Name": "hello", "Int": "3"}`)
|
||||
c Conf
|
||||
)
|
||||
assert.Error(t, UnmarshalJsonBytes(input, &c))
|
||||
}
|
||||
|
||||
func TestUnmarshalJsonBytesWithAnonymousFieldBadTagInStruct(t *testing.T) {
|
||||
type (
|
||||
InnerConf struct {
|
||||
Name string `json:",optional=123"`
|
||||
}
|
||||
|
||||
Conf struct {
|
||||
InnerConf `json:",optional"`
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
input = []byte(`{"Name": "hello"}`)
|
||||
c Conf
|
||||
)
|
||||
assert.Error(t, UnmarshalJsonBytes(input, &c))
|
||||
}
|
||||
|
||||
func TestUnmarshalJsonBytesWithAnonymousFieldNotInOptions(t *testing.T) {
|
||||
type (
|
||||
InnerConf struct {
|
||||
Name string `json:",options=[a,b]"`
|
||||
}
|
||||
|
||||
Conf struct {
|
||||
InnerConf `json:",optional"`
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
input = []byte(`{"Name": "hello"}`)
|
||||
c Conf
|
||||
)
|
||||
assert.Error(t, UnmarshalJsonBytes(input, &c))
|
||||
}
|
||||
|
||||
func BenchmarkDefaultValue(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
var a struct {
|
||||
|
||||
@@ -31,3 +31,27 @@ func TestMapValuerWithInherit_Value(t *testing.T) {
|
||||
assert.Equal(t, "localhost", m["host"])
|
||||
assert.Equal(t, 8080, m["port"])
|
||||
}
|
||||
|
||||
func TestRecursiveValuer_Value(t *testing.T) {
|
||||
input := map[string]interface{}{
|
||||
"component": map[string]interface{}{
|
||||
"name": "test",
|
||||
"foo": map[string]interface{}{
|
||||
"bar": "baz",
|
||||
},
|
||||
},
|
||||
"foo": "value",
|
||||
}
|
||||
valuer := recursiveValuer{
|
||||
current: mapValuer(input["component"].(map[string]interface{})),
|
||||
parent: simpleValuer{
|
||||
current: mapValuer(input),
|
||||
},
|
||||
}
|
||||
|
||||
val, ok := valuer.Value("foo")
|
||||
assert.True(t, ok)
|
||||
assert.EqualValues(t, map[string]interface{}{
|
||||
"bar": "baz",
|
||||
}, val)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user