When the nested structure has the same name field, the resolution skips the same name field of the substructure

This commit is contained in:
liubin 2021-12-27 16:48:16 +08:00
parent d062a6a615
commit e1e6bb07f7
2 changed files with 105 additions and 3 deletions

View File

@ -78,11 +78,23 @@ func mappingByPtr(ptr interface{}, setter setter, tag string) error {
return err return err
} }
func mapping(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) { type structInfo struct {
value reflect.Value
field reflect.StructField
}
func mapping(value reflect.Value, field reflect.StructField, setter setter, tag string, maped ...map[string]struct{}) (bool, error) {
if field.Tag.Get(tag) == "-" { // just ignoring this field if field.Tag.Get(tag) == "-" { // just ignoring this field
return false, nil return false, nil
} }
var _maped map[string]struct{}
if len(maped) > 0 {
_maped = maped[0]
} else {
_maped = make(map[string]struct{})
}
vKind := value.Kind() vKind := value.Kind()
if vKind == reflect.Ptr { if vKind == reflect.Ptr {
@ -92,7 +104,7 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag
isNew = true isNew = true
vPtr = reflect.New(value.Type().Elem()) vPtr = reflect.New(value.Type().Elem())
} }
isSet, err := mapping(vPtr.Elem(), field, setter, tag) isSet, err := mapping(vPtr.Elem(), field, setter, tag, _maped)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -116,22 +128,59 @@ func mapping(value reflect.Value, field reflect.StructField, setter setter, tag
tValue := value.Type() tValue := value.Type()
var isSet bool var isSet bool
structs := make([]structInfo, 0)
for i := 0; i < value.NumField(); i++ { for i := 0; i < value.NumField(); i++ {
sf := tValue.Field(i) sf := tValue.Field(i)
if sf.PkgPath != "" && !sf.Anonymous { // unexported if sf.PkgPath != "" && !sf.Anonymous { // unexported
continue continue
} }
ok, err := mapping(value.Field(i), sf, setter, tag) tagValue := sf.Tag.Get(tag)
if _, ok := _maped[tagValue]; ok {
continue
}
if isStruct(value.Field(i)) {
structs = append(structs, structInfo{
value: value.Field(i),
field: sf,
})
continue
}
ok, err := mapping(value.Field(i), sf, setter, tag, _maped)
if err != nil { if err != nil {
return false, err return false, err
} }
isSet = isSet || ok isSet = isSet || ok
if isSet && tagValue != "" {
_maped[tagValue] = struct{}{}
}
}
for _, st := range structs {
ok, err := mapping(st.value, st.field, setter, tag, _maped)
if err != nil {
return false, err
}
isSet = isSet || ok
tagValue := st.field.Tag.Get(tag)
if isSet && tagValue != "" {
_maped[tagValue] = struct{}{}
}
} }
return isSet, nil return isSet, nil
} }
return false, nil return false, nil
} }
func isStruct(_field reflect.Value) bool {
if _field.Kind() == reflect.Ptr {
vPtr := reflect.New(_field.Type().Elem())
return vPtr.Elem().Kind() == reflect.Struct
} else {
return _field.Kind() == reflect.Struct
}
}
type setOptions struct { type setOptions struct {
isDefaultExists bool isDefaultExists bool
defaultValue string defaultValue string

View File

@ -288,3 +288,56 @@ func TestMappingIgnoredCircularRef(t *testing.T) {
err := mappingByPtr(&s, formSource{}, "form") err := mappingByPtr(&s, formSource{}, "form")
assert.NoError(t, err) assert.NoError(t, err)
} }
func TestMappingNestedStructure(t *testing.T) {
formData := formSource{"name": {"hello"}, "id": {"1"}, "age": {"18"}}
type Other struct {
Name string `form:"name"`
}
type City struct {
Id int `form:"id"`
Name string `form:"name"`
OtherInfo *Other
}
type User struct {
Id int `form:"id"`
Name string `form:"name"`
Age int `form:"age"`
CityInfo *City
}
var u User
err := mappingByPtr(&u, formData, "form")
assert.NoError(t, err)
assert.Equal(t, u.Id, 1)
assert.Equal(t, u.Name, "hello")
assert.Nil(t, u.CityInfo)
type User1 struct {
CityInfo *City
Id int `form:"id"`
Name string `form:"name"`
Age int `form:"age"`
}
var u1 User1
err = mappingByPtr(&u1, formData, "form")
assert.NoError(t, err)
assert.Equal(t, u1.Id, 1)
assert.Equal(t, u1.Name, "hello")
assert.Nil(t, u1.CityInfo)
type User2 struct {
CityInfo City
Id int `form:"id"`
Name string `form:"name"`
Age int `form:"age"`
}
var u2 User2
err = mappingByPtr(&u2, formData, "form")
assert.NoError(t, err)
assert.Equal(t, u2.Id, 1)
assert.Equal(t, u2.Name, "hello")
assert.Equal(t, u2.CityInfo.Id, 0)
assert.Equal(t, u2.CityInfo.Name, "")
}