mirror of
https://gitee.com/johng/gf
synced 2026-06-06 16:21:40 +08:00
improve function Struct/StructDeep for package gconv
This commit is contained in:
@ -48,6 +48,7 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string]
|
||||
m := make(map[string]interface{})
|
||||
switch r := value.(type) {
|
||||
case string:
|
||||
// If it is a JSON string, automatically unmarshal it!
|
||||
if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' {
|
||||
if err := json.Unmarshal([]byte(r), &m); err != nil {
|
||||
return nil
|
||||
@ -56,6 +57,7 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string]
|
||||
return nil
|
||||
}
|
||||
case []byte:
|
||||
// If it is a JSON string, automatically unmarshal it!
|
||||
if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' {
|
||||
if err := json.Unmarshal(r, &m); err != nil {
|
||||
return nil
|
||||
@ -121,8 +123,9 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string]
|
||||
for k, v := range r {
|
||||
m[String(k)] = v
|
||||
}
|
||||
// Not a common type, it then uses reflection for conversion.
|
||||
|
||||
default:
|
||||
// Not a common type, it then uses reflection for conversion.
|
||||
var rv reflect.Value
|
||||
if v, ok := value.(reflect.Value); ok {
|
||||
rv = v
|
||||
@ -137,10 +140,9 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string]
|
||||
}
|
||||
switch kind {
|
||||
// If <value> is type of array, it converts the value of even number index as its key and
|
||||
// the value of odd number index as its corresponding value.
|
||||
// Eg:
|
||||
// the value of odd number index as its corresponding value, for example:
|
||||
// []string{"k1","v1","k2","v2"} => map[string]interface{}{"k1":"v1", "k2":"v2"}
|
||||
// []string{"k1","v1","k2"} => map[string]interface{}{"k1":"v1", "k2":nil}
|
||||
// []string{"k1","v1","k2"} => map[string]interface{}{"k1":"v1", "k2":nil}
|
||||
case reflect.Slice, reflect.Array:
|
||||
length := rv.Len()
|
||||
for i := 0; i < length; i += 2 {
|
||||
|
||||
@ -38,6 +38,17 @@ var (
|
||||
// in mapping procedure to do the matching.
|
||||
// It ignores the map key, if it does not match.
|
||||
func Struct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return doStruct(params, pointer, false, mapping...)
|
||||
}
|
||||
|
||||
// StructDeep do Struct function recursively.
|
||||
// See Struct.
|
||||
func StructDeep(params interface{}, pointer interface{}, mapping ...map[string]string) error {
|
||||
return doStruct(params, pointer, true, mapping...)
|
||||
}
|
||||
|
||||
// doStruct is the core internal converting function for any data to struct recursively or not.
|
||||
func doStruct(params interface{}, pointer interface{}, recursive bool, mapping ...map[string]string) (err error) {
|
||||
if params == nil {
|
||||
return errors.New("params cannot be nil")
|
||||
}
|
||||
@ -52,7 +63,7 @@ func Struct(params interface{}, pointer interface{}, mapping ...map[string]strin
|
||||
}()
|
||||
|
||||
// paramsMap is the map[string]interface{} type variable for params.
|
||||
paramsMap := Map(params)
|
||||
paramsMap := MapDeep(params)
|
||||
if paramsMap == nil {
|
||||
return fmt.Errorf("invalid params: %v", params)
|
||||
}
|
||||
@ -108,9 +119,9 @@ func Struct(params interface{}, pointer interface{}, mapping ...map[string]strin
|
||||
if len(mapping) > 0 && len(mapping[0]) > 0 {
|
||||
for mapK, mapV := range mapping[0] {
|
||||
// mapV is the the attribute name of the struct.
|
||||
if v, ok := paramsMap[mapK]; ok {
|
||||
if paramV, ok := paramsMap[mapK]; ok {
|
||||
doneMap[mapV] = struct{}{}
|
||||
if err := bindVarToStructAttr(elem, mapV, v); err != nil {
|
||||
if err := bindVarToStructAttr(elem, mapV, paramV, recursive, mapping...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -125,12 +136,20 @@ func Struct(params interface{}, pointer interface{}, mapping ...map[string]strin
|
||||
tempName = ""
|
||||
)
|
||||
for i := 0; i < elem.NumField(); i++ {
|
||||
field := elemType.Field(i)
|
||||
// Only do converting to public attributes.
|
||||
if !utils.IsLetterUpper(elemType.Field(i).Name[0]) {
|
||||
if !utils.IsLetterUpper(field.Name[0]) {
|
||||
continue
|
||||
}
|
||||
tempName = elemType.Field(i).Name
|
||||
attrMap[tempName] = replaceCharReg.ReplaceAllString(tempName, "")
|
||||
// Maybe it's struct/*struct.
|
||||
if recursive && field.Anonymous {
|
||||
if err = doStruct(paramsMap, elem.Field(i), recursive, mapping...); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
tempName = field.Name
|
||||
attrMap[tempName] = replaceCharReg.ReplaceAllString(tempName, "")
|
||||
}
|
||||
}
|
||||
if len(attrMap) == 0 {
|
||||
return nil
|
||||
@ -163,15 +182,17 @@ func Struct(params interface{}, pointer interface{}, mapping ...map[string]strin
|
||||
}
|
||||
|
||||
// Matching the parameters to struct attributes.
|
||||
for attrKey, cmpKey := range attrMap {
|
||||
// Eg:
|
||||
// UserName eq user_name
|
||||
// User-Name eq username
|
||||
// username eq userName
|
||||
// etc.
|
||||
if strings.EqualFold(checkName, cmpKey) {
|
||||
attrName = attrKey
|
||||
break
|
||||
if attrName == "" {
|
||||
for attrKey, cmpKey := range attrMap {
|
||||
// Eg:
|
||||
// UserName eq user_name
|
||||
// User-Name eq username
|
||||
// username eq userName
|
||||
// etc.
|
||||
if strings.EqualFold(checkName, cmpKey) {
|
||||
attrName = attrKey
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -185,54 +206,15 @@ func Struct(params interface{}, pointer interface{}, mapping ...map[string]strin
|
||||
}
|
||||
// Mark it done.
|
||||
doneMap[attrName] = struct{}{}
|
||||
if err := bindVarToStructAttr(elem, attrName, mapV); err != nil {
|
||||
if err := bindVarToStructAttr(elem, attrName, mapV, recursive, mapping...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StructDeep do Struct function recursively.
|
||||
// See Struct.
|
||||
func StructDeep(params interface{}, pointer interface{}, mapping ...map[string]string) error {
|
||||
if params == nil {
|
||||
return nil
|
||||
}
|
||||
if err := Struct(params, pointer, mapping...); err != nil {
|
||||
return err
|
||||
} else {
|
||||
rv, ok := pointer.(reflect.Value)
|
||||
if !ok {
|
||||
rv = reflect.ValueOf(pointer)
|
||||
}
|
||||
kind := rv.Kind()
|
||||
for kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
case reflect.Struct:
|
||||
rt := rv.Type()
|
||||
for i := 0; i < rv.NumField(); i++ {
|
||||
// Only do converting to public attributes.
|
||||
if !utils.IsLetterUpper(rt.Field(i).Name[0]) {
|
||||
continue
|
||||
}
|
||||
trv := rv.Field(i)
|
||||
switch trv.Kind() {
|
||||
case reflect.Struct:
|
||||
if err := StructDeep(params, trv, mapping...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindVarToStructAttr sets value to struct object attribute by name.
|
||||
func bindVarToStructAttr(elem reflect.Value, name string, value interface{}) (err error) {
|
||||
func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, recursive bool, mapping ...map[string]string) (err error) {
|
||||
structFieldValue := elem.FieldByName(name)
|
||||
if !structFieldValue.IsValid() {
|
||||
return nil
|
||||
@ -243,7 +225,7 @@ func bindVarToStructAttr(elem reflect.Value, name string, value interface{}) (er
|
||||
}
|
||||
defer func() {
|
||||
if recover() != nil {
|
||||
err = bindVarToReflectValue(structFieldValue, value)
|
||||
err = bindVarToReflectValue(structFieldValue, value, recursive, mapping...)
|
||||
}
|
||||
}()
|
||||
if empty.IsNil(value) {
|
||||
@ -255,7 +237,7 @@ func bindVarToStructAttr(elem reflect.Value, name string, value interface{}) (er
|
||||
}
|
||||
|
||||
// bindVarToReflectValue sets <value> to reflect value object <structFieldValue>.
|
||||
func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) (err error) {
|
||||
func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, recursive bool, mapping ...map[string]string) (err error) {
|
||||
kind := structFieldValue.Kind()
|
||||
|
||||
// Converting using interface, for some kinds.
|
||||
@ -282,10 +264,12 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) (e
|
||||
return v.UnmarshalValue(value)
|
||||
}
|
||||
|
||||
if err := Struct(value, structFieldValue); err != nil {
|
||||
// Recursively converting for struct attribute.
|
||||
if err := doStruct(value, structFieldValue, recursive); err != nil {
|
||||
// Note there's reflect conversion mechanism here.
|
||||
structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
|
||||
}
|
||||
|
||||
// Note that the slice element might be type of struct,
|
||||
// so it uses Struct function doing the converting internally.
|
||||
case reflect.Slice, reflect.Array:
|
||||
@ -298,14 +282,14 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) (e
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if t.Kind() == reflect.Ptr {
|
||||
e := reflect.New(t.Elem()).Elem()
|
||||
if err := Struct(v.Index(i).Interface(), e); err != nil {
|
||||
if err := doStruct(v.Index(i).Interface(), e, recursive); err != nil {
|
||||
// Note there's reflect conversion mechanism here.
|
||||
e.Set(reflect.ValueOf(v.Index(i).Interface()).Convert(t))
|
||||
}
|
||||
a.Index(i).Set(e.Addr())
|
||||
} else {
|
||||
e := reflect.New(t).Elem()
|
||||
if err := Struct(v.Index(i).Interface(), e); err != nil {
|
||||
if err := doStruct(v.Index(i).Interface(), e, recursive); err != nil {
|
||||
// Note there's reflect conversion mechanism here.
|
||||
e.Set(reflect.ValueOf(v.Index(i).Interface()).Convert(t))
|
||||
}
|
||||
@ -318,14 +302,14 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) (e
|
||||
t := a.Index(0).Type()
|
||||
if t.Kind() == reflect.Ptr {
|
||||
e := reflect.New(t.Elem()).Elem()
|
||||
if err := Struct(value, e); err != nil {
|
||||
if err := doStruct(value, e, recursive); err != nil {
|
||||
// Note there's reflect conversion mechanism here.
|
||||
e.Set(reflect.ValueOf(value).Convert(t))
|
||||
}
|
||||
a.Index(0).Set(e.Addr())
|
||||
} else {
|
||||
e := reflect.New(t).Elem()
|
||||
if err := Struct(value, e); err != nil {
|
||||
if err := doStruct(value, e, recursive); err != nil {
|
||||
// Note there's reflect conversion mechanism here.
|
||||
e.Set(reflect.ValueOf(value).Convert(t))
|
||||
}
|
||||
@ -344,7 +328,7 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) (e
|
||||
return err
|
||||
}
|
||||
elem := item.Elem()
|
||||
if err = bindVarToReflectValue(elem, value); err == nil {
|
||||
if err = bindVarToReflectValue(elem, value, recursive, mapping...); err == nil {
|
||||
structFieldValue.Set(elem.Addr())
|
||||
}
|
||||
|
||||
|
||||
@ -491,6 +491,43 @@ func Test_StructDeep3(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// https://github.com/gogf/gf/issues/775
|
||||
func Test_StructDeep4(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type Sub2 struct {
|
||||
SubName string
|
||||
}
|
||||
type sub1 struct {
|
||||
Sub2
|
||||
Name string
|
||||
}
|
||||
type Test struct {
|
||||
Sub sub1 `json:"sub"`
|
||||
}
|
||||
|
||||
data := `{
|
||||
"sub": {
|
||||
"map":{"k":"v"},
|
||||
"Name": "name",
|
||||
"SubName": "subname"
|
||||
}}`
|
||||
|
||||
expect := Test{
|
||||
Sub: sub1{
|
||||
Name: "name",
|
||||
Sub2: Sub2{
|
||||
SubName: "subname",
|
||||
},
|
||||
},
|
||||
}
|
||||
tx := new(Test)
|
||||
if err := gconv.StructDeep(data, &tx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
t.Assert(tx, expect)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Struct_Time(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
|
||||
Reference in New Issue
Block a user