From 302f3c1467917382cc73a087fbf39a8b31719f39 Mon Sep 17 00:00:00 2001 From: John Date: Mon, 14 Jan 2019 13:55:07 +0800 Subject: [PATCH] optimized package gconv, supported struct attr with ptr --- g/util/gconv/gconv_struct.go | 69 ++++++++++++------ .../{gconv_test.go => gconv_z_bench_test.go} | 0 g/util/gconv/gconv_z_unit_struct_test.go | 72 +++++++++++++++++++ geg/other/test2.go | 33 ++++++--- geg/util/gconv/gconv_struct6.go | 2 +- 5 files changed, 143 insertions(+), 33 deletions(-) rename g/util/gconv/{gconv_test.go => gconv_z_bench_test.go} (100%) create mode 100644 g/util/gconv/gconv_z_unit_struct_test.go diff --git a/g/util/gconv/gconv_struct.go b/g/util/gconv/gconv_struct.go index e34649e98..f02389a93 100644 --- a/g/util/gconv/gconv_struct.go +++ b/g/util/gconv/gconv_struct.go @@ -7,13 +7,13 @@ package gconv import ( - "gitee.com/johng/gf/g/container/gset" - "gitee.com/johng/gf/g/util/gstr" - "reflect" - "gitee.com/johng/gf/third/github.com/fatih/structs" - "strings" "errors" "fmt" + "gitee.com/johng/gf/g/container/gset" + "gitee.com/johng/gf/g/util/gstr" + "gitee.com/johng/gf/third/github.com/fatih/structs" + "reflect" + "strings" ) // 将params键值对参数映射到对应的struct对象属性上,第三个参数mapping为非必需,表示自定义名称与属性名称的映射关系。 @@ -63,7 +63,7 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string for mappingk, mappingv := range attrMapping[0] { if v, ok := paramsMap[mappingk]; ok { dmap[mappingv] = true - if err := bindVarToStruct(elem, mappingv, v); err != nil { + if err := bindVarToStructAttr(elem, mappingv, v); err != nil { return err } } @@ -78,7 +78,7 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string } if v, ok := paramsMap[tagk]; ok { dmap[tagv] = true - if err := bindVarToStruct(elem, tagv, v); err != nil { + if err := bindVarToStructAttr(elem, tagv, v); err != nil { return err } } @@ -124,7 +124,7 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string if name == "" { continue } - if err := bindVarToStruct(elem, name, mapv); err != nil { + if err := bindVarToStructAttr(elem, name, mapv); err != nil { return err } } @@ -153,7 +153,7 @@ func getTagMapOfStruct(objPointer interface{}) map[string]string { } // 将参数值绑定到对象指定名称的属性上 -func bindVarToStruct(elem reflect.Value, name string, value interface{}) (err error) { +func bindVarToStructAttr(elem reflect.Value, name string, value interface{}) (err error) { structFieldValue := elem.FieldByName(name) // 键名与对象属性匹配检测,map中如果有struct不存在的属性,那么不做处理,直接return if !structFieldValue.IsValid() { @@ -167,7 +167,7 @@ func bindVarToStruct(elem reflect.Value, name string, value interface{}) (err er defer func() { // 如果转换失败,那么可能是类型不匹配造成(例如属性包含自定义类型),那么执行递归转换 if recover() != nil { - err = bindVarToStructIfDefaultConvertionFailed(structFieldValue, value) + err = bindVarToReflectValue(structFieldValue, value) } }() structFieldValue.Set(reflect.ValueOf(Convert(value, structFieldValue.Type().String()))) @@ -189,37 +189,62 @@ func bindVarToStructByIndex(elem reflect.Value, index int, value interface{}) (e defer func() { // 如果转换失败,那么可能是类型不匹配造成(例如属性包含自定义类型),那么执行递归转换 if recover() != nil { - err = bindVarToStructIfDefaultConvertionFailed(structFieldValue, value) + err = bindVarToReflectValue(structFieldValue, value) } }() structFieldValue.Set(reflect.ValueOf(Convert(value, structFieldValue.Type().String()))) return nil } -// 当默认的基本类型转换失败时,通过recover判断后执行反射类型转换 -func bindVarToStructIfDefaultConvertionFailed(structFieldValue reflect.Value, value interface{}) error { +// 当默认的基本类型转换失败时,通过recover判断后执行反射类型转换(处理复杂类型) +func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) error { switch structFieldValue.Kind() { + // 属性为结构体 case reflect.Struct: Struct(value, structFieldValue) + // 属性为数组类型 case reflect.Slice: fallthrough case reflect.Array: a := reflect.Value{} v := reflect.ValueOf(value) if v.Kind() == reflect.Slice || v.Kind() == reflect.Array { - a = reflect.MakeSlice(structFieldValue.Type(), v.Len(), v.Len()) - for i := 0; i < v.Len(); i++ { - n := reflect.New(structFieldValue.Type().Elem()).Elem() - Struct(v.Index(i).Interface(), n) - a.Index(i).Set(n) + if v.Len() > 0 { + a = reflect.MakeSlice(structFieldValue.Type(), v.Len(), v.Len()) + t := a.Index(0).Type() + for i := 0; i < v.Len(); i++ { + if t.Kind() == reflect.Ptr { + e := reflect.New(t.Elem()).Elem() + Struct(v.Index(i).Interface(), e) + a.Index(i).Set(e.Addr()) + } else { + e := reflect.New(t).Elem() + Struct(v.Index(i).Interface(), e) + a.Index(i).Set(e) + } + } } } else { - a = reflect.MakeSlice(structFieldValue.Type(), 1, 1) - n := reflect.New(structFieldValue.Type().Elem()).Elem() - Struct(value, n) - a.Index(0).Set(n) + a = reflect.MakeSlice(structFieldValue.Type(), 1, 1) + t := a.Index(0).Type() + if t.Kind() == reflect.Ptr { + e := reflect.New(t.Elem()).Elem() + Struct(value, e) + a.Index(0).Set(e.Addr()) + } else { + e := reflect.New(t).Elem() + Struct(value, e) + a.Index(0).Set(e) + } } structFieldValue.Set(a) + + // 属性为指针类型 + case reflect.Ptr: + e := reflect.New(structFieldValue.Type().Elem()).Elem() + Struct(value, e) + structFieldValue.Set(e.Addr()) + default: return errors.New(fmt.Sprintf(`cannot convert to type "%s"`, structFieldValue.Type().String())) } diff --git a/g/util/gconv/gconv_test.go b/g/util/gconv/gconv_z_bench_test.go similarity index 100% rename from g/util/gconv/gconv_test.go rename to g/util/gconv/gconv_z_bench_test.go diff --git a/g/util/gconv/gconv_z_unit_struct_test.go b/g/util/gconv/gconv_z_unit_struct_test.go new file mode 100644 index 000000000..86ff3cb2b --- /dev/null +++ b/g/util/gconv/gconv_z_unit_struct_test.go @@ -0,0 +1,72 @@ +// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://gitee.com/johng/gf. + +package gconv_test + +import ( + "gitee.com/johng/gf/g" + "gitee.com/johng/gf/g/util/gconv" + "gitee.com/johng/gf/g/util/gtest" + "testing" +) + + +func Test_Struct_Basic(t *testing.T) { + gtest.Case(t, func() { + type User struct { + Uid int + Name string + Site_Url string + NickName string + Pass1 string `gconv:"password1"` + Pass2 string `gconv:"password2"` + } + user := (*User)(nil) + // 使用默认映射规则绑定属性值到对象 + user = new(User) + params1 := g.Map{ + "uid" : 1, + "Name" : "john", + "siteurl" : "https://gfer.me", + "nick_name" : "johng", + "PASS1" : "123", + "PASS2" : "456", + } + if err := gconv.Struct(params1, user); err != nil { + gtest.Error(err) + } + gtest.Assert(user, &User{ + Uid : 1, + Name : "john", + Site_Url : "https://gfer.me", + NickName : "johng", + Pass1 : "123", + Pass2 : "456", + }) + + // 使用struct tag映射绑定属性值到对象 + user = new(User) + params2 := g.Map { + "uid" : 2, + "name" : "smith", + "site-url" : "https://gfer.me", + "nick name" : "johng", + "password1" : "111", + "password2" : "222", + } + if err := gconv.Struct(params2, user); err != nil { + gtest.Error(err) + } + gtest.Assert(user, &User{ + Uid : 2, + Name : "smith", + Site_Url : "https://gfer.me", + NickName : "johng", + Pass1 : "111", + Pass2 : "222", + }) + }) +} diff --git a/geg/other/test2.go b/geg/other/test2.go index 2c541bd7e..8c4f76ecb 100644 --- a/geg/other/test2.go +++ b/geg/other/test2.go @@ -2,18 +2,31 @@ package main import ( "fmt" + "gitee.com/johng/gf/g" "gitee.com/johng/gf/g/util/gconv" - "time" ) -func main(){ - type Test struct { - Date time.Time `json:"date"` +func main() { + type Score struct { + Name string + Result int } - o := new(Test) - m := map[string]interface{}{ - "Date" : "", + type User struct { + Scores []*Score } - gconv.Struct(m, o) - fmt.Println(o) -} + + user := new(User) + scores := map[string]interface{}{ + "Scores" : map[string]interface{}{ + "Name" : "john", + "Result" : 100, + }, + } + + // 嵌套struct转换,属性为slice类型,数值为map类型 + if err := gconv.Struct(scores, user); err != nil { + fmt.Println(err) + } else { + g.Dump(user) + } +} \ No newline at end of file diff --git a/geg/util/gconv/gconv_struct6.go b/geg/util/gconv/gconv_struct6.go index 5e31d47e7..3e932984a 100644 --- a/geg/util/gconv/gconv_struct6.go +++ b/geg/util/gconv/gconv_struct6.go @@ -12,7 +12,7 @@ func main() { Result int } type User struct { - Scores []Score + Scores []*Score } user := new(User)