improve function Struct/StructDeep for package gconv

This commit is contained in:
John
2020-07-08 10:52:45 +08:00
parent adbaa4aa89
commit 3643a69d8d
3 changed files with 91 additions and 68 deletions

View File

@ -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 {

View File

@ -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())
}

View File

@ -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 {