mirror of https://github.com/gin-gonic/gin.git
When the nested structure has the same name field, the resolution skips the same name field of the substructure
This commit is contained in:
parent
d062a6a615
commit
e1e6bb07f7
|
@ -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
|
||||||
|
|
|
@ -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, "")
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue