From 41f33af51b3383a2e79e588c213f7591e2f27fe1 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 28 May 2019 21:41:00 +0800 Subject: [PATCH] add gconv.StructDeep/MapDeep functions for gconv; add struct inherit converting support for gdb --- g/container/gvar/gvar.go | 6 +- g/database/gdb/gdb_base.go | 8 +- g/database/gdb/gdb_func.go | 49 ++++---- g/database/gdb/gdb_model.go | 4 +- g/database/gdb/gdb_type_record.go | 7 +- .../gdb/gdb_unit_struct_inherit_test.go | 99 ++++++++++++++++ g/net/ghttp/ghttp_request_post.go | 8 +- g/net/ghttp/ghttp_request_query.go | 6 +- g/net/ghttp/ghttp_request_request.go | 6 +- g/util/gconv/gconv_map.go | 40 +++++-- g/util/gconv/gconv_struct.go | 106 ++++++++++++------ g/util/gconv/gconv_z_unit_map_test.go | 47 ++++---- g/util/gconv/gconv_z_unit_struct_test.go | 42 ++++++- 13 files changed, 314 insertions(+), 114 deletions(-) create mode 100644 g/database/gdb/gdb_unit_struct_inherit_test.go diff --git a/g/container/gvar/gvar.go b/g/container/gvar/gvar.go index 16e573fe0..62e2ea20a 100644 --- a/g/container/gvar/gvar.go +++ b/g/container/gvar/gvar.go @@ -80,9 +80,9 @@ func (v *Var) GTime(format...string) *gtime.Time { // Struct maps value of to . // The param should be a pointer to a struct instance. -// The param is used to specify the key-to-attribute mapping rules. -func (v *Var) Struct(objPointer interface{}, attrMapping...map[string]string) error { - return gconv.Struct(v.Val(), objPointer, attrMapping...) +// The param is used to specify the key-to-attribute mapping rules. +func (v *Var) Struct(pointer interface{}, mapping...map[string]string) error { + return gconv.Struct(v.Val(), pointer, mapping...) } func (v *Var) IsNil() bool { return v.Val() == nil } diff --git a/g/database/gdb/gdb_base.go b/g/database/gdb/gdb_base.go index d16be23d1..ebc745c49 100644 --- a/g/database/gdb/gdb_base.go +++ b/g/database/gdb/gdb_base.go @@ -312,7 +312,7 @@ func (bs *dbBase) doInsert(link dbLink, table string, data interface{}, option i return bs.db.doBatchInsert(link, table, data, option, batch...) case reflect.Map: fallthrough case reflect.Struct: - dataMap = gconv.Map(data) + dataMap = structToMap(data) default: return result, errors.New(fmt.Sprint("unsupported data type:", kind)) } @@ -390,11 +390,11 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, opt case reflect.Array: listMap = make(List, rv.Len()) for i := 0; i < rv.Len(); i++ { - listMap[i] = gconv.Map(rv.Index(i).Interface()) + listMap[i] = structToMap(rv.Index(i).Interface()) } case reflect.Map: fallthrough case reflect.Struct: - listMap = List{Map(gconv.Map(list))} + listMap = List{Map(structToMap(list))} default: return result, errors.New(fmt.Sprint("unsupported list type:", kind)) } @@ -504,7 +504,7 @@ func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, conditio case reflect.Map: fallthrough case reflect.Struct: var fields []string - for k, v := range gconv.Map(data) { + for k, v := range structToMap(data) { fields = append(fields, fmt.Sprintf("%s%s%s=?", charL, k, charR)) params = append(params, convertParam(v)) } diff --git a/g/database/gdb/gdb_func.go b/g/database/gdb/gdb_func.go index e7e8b8181..374def4ee 100644 --- a/g/database/gdb/gdb_func.go +++ b/g/database/gdb/gdb_func.go @@ -7,16 +7,16 @@ package gdb import ( - "bytes" - "errors" - "fmt" - "github.com/gogf/gf/g/os/glog" - "github.com/gogf/gf/g/os/gtime" - "github.com/gogf/gf/g/text/gregex" - "github.com/gogf/gf/g/text/gstr" - "github.com/gogf/gf/g/util/gconv" - "reflect" - "strings" + "bytes" + "errors" + "fmt" + "github.com/gogf/gf/g/os/glog" + "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/text/gregex" + "github.com/gogf/gf/g/text/gstr" + "github.com/gogf/gf/g/util/gconv" + "reflect" + "strings" "time" ) @@ -41,7 +41,7 @@ func formatCondition(where interface{}, args []interface{}) (newWhere string, ne // map/struct类型 case reflect.Map: fallthrough case reflect.Struct: - for key, value := range gconv.Map(where) { + for key, value := range structToMap(where) { if buffer.Len() > 0 { buffer.WriteString(" AND ") } @@ -170,12 +170,12 @@ func printSql(v *Sql) { // 格式化错误信息 func formatError(err error, query string, args ...interface{}) error { if err != nil { - errstr := fmt.Sprintf("DB ERROR: %s\n", err.Error()) - errstr += fmt.Sprintf("DB QUERY: %s\n", query) + errStr := fmt.Sprintf("DB ERROR: %s\n", err.Error()) + errStr += fmt.Sprintf("DB QUERY: %s\n", query) if len(args) > 0 { - errstr += fmt.Sprintf("DB PARAM: %v\n", args) + errStr += fmt.Sprintf("DB PARAM: %v\n", args) } - err = errors.New(errstr) + err = errors.New(errStr) } return err } @@ -197,8 +197,8 @@ func getInsertOperationByOption(option int) string { // 该方法用于将变量传递给数据库执行之前。 func structToMap(obj interface{}) map[string]interface{} { data := gconv.Map(obj) - for k, v := range data { - rv := reflect.ValueOf(v) + for key, value := range data { + rv := reflect.ValueOf(value) kind := rv.Kind() if kind == reflect.Ptr { rv = rv.Elem() @@ -207,17 +207,24 @@ func structToMap(obj interface{}) map[string]interface{} { switch kind { case reflect.Struct: // 底层数据库引擎支持 time.Time 类型 - if _, ok := v.(time.Time); ok { + if _, ok := value.(time.Time); ok { continue } // 如果执行String方法,那么执行字符串转换 - if s, ok := v.(apiString); ok { - data[k] = s.String() + if s, ok := value.(apiString); ok { + data[key] = s.String() + continue } - for k, v := range structToMap(v) { + delete(data, key) + for k, v := range structToMap(value) { data[k] = v } } } return data +} + +// 使用递归的方式将map键值对映射到struct对象上,注意参数是一个指向struct的指针。 +func mapToStruct(data map[string]interface{}, pointer interface{}) error { + return gconv.StructDeep(data, pointer) } \ No newline at end of file diff --git a/g/database/gdb/gdb_model.go b/g/database/gdb/gdb_model.go index f039a64ae..e53044cde 100644 --- a/g/database/gdb/gdb_model.go +++ b/g/database/gdb/gdb_model.go @@ -268,12 +268,12 @@ func (md *Model) Data(data ...interface{}) *Model { case reflect.Array: list := make(List, rv.Len()) for i := 0; i < rv.Len(); i++ { - list[i] = gconv.Map(rv.Index(i).Interface()) + list[i] = structToMap(rv.Index(i).Interface()) } model.data = list case reflect.Map: fallthrough case reflect.Struct: - model.data = Map(gconv.Map(data[0])) + model.data = Map(structToMap(data[0])) default: model.data = data[0] } diff --git a/g/database/gdb/gdb_type_record.go b/g/database/gdb/gdb_type_record.go index 16e3f51c9..8d9597666 100644 --- a/g/database/gdb/gdb_type_record.go +++ b/g/database/gdb/gdb_type_record.go @@ -7,8 +7,7 @@ package gdb import ( - "github.com/gogf/gf/g/encoding/gparser" - "github.com/gogf/gf/g/util/gconv" + "github.com/gogf/gf/g/encoding/gparser" ) // 将记录结果转换为JSON字符串 @@ -33,6 +32,6 @@ func (r Record) ToMap() Map { } // 将Map变量映射到指定的struct对象中,注意参数应当是一个对象的指针 -func (r Record) ToStruct(objPointer interface{}) error { - return gconv.Struct(r.ToMap(), objPointer) +func (r Record) ToStruct(pointer interface{}) error { + return mapToStruct(r.ToMap(), pointer) } diff --git a/g/database/gdb/gdb_unit_struct_inherit_test.go b/g/database/gdb/gdb_unit_struct_inherit_test.go new file mode 100644 index 000000000..dfdd09eef --- /dev/null +++ b/g/database/gdb/gdb_unit_struct_inherit_test.go @@ -0,0 +1,99 @@ +// Copyright 2019 gf Author(https://github.com/gogf/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://github.com/gogf/gf. + +package gdb_test + +import ( + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/test/gtest" + "testing" +) + +func TestModel_Inherit_Insert(t *testing.T) { + gtest.Case(t, func() { + type Base struct { + Id int `json:"id"` + Uid int `json:"uid"` + CreateTime string `json:"create_time"` + } + type User struct { + Base + Passport string `json:"passport"` + Password string `json:"password"` + Nickname string `json:"nickname"` + } + result, err := db.Table("user").Filter().Data(User{ + Passport : "john-test", + Password : "123456", + Nickname : "John", + Base : Base { + Id : 100, + Uid : 100, + CreateTime : gtime.Now().String(), + }, + }).Insert() + gtest.Assert(err, nil) + n, _ := result.RowsAffected() + gtest.Assert(n, 1) + value, err := db.Table("user").Fields("passport").Where("id=100").Value() + gtest.Assert(err, nil) + gtest.Assert(value.String(), "john-test") + // Delete this test data. + _, err = db.Table("user").Where("id", 100).Delete() + gtest.Assert(err, nil) + }) +} + +func TestModel_Inherit_MapToStruct(t *testing.T) { + gtest.Case(t, func() { + type Ids struct { + Id int `json:"id"` + Uid int `json:"uid"` + } + type Base struct { + Ids + CreateTime string `json:"create_time"` + } + type User struct { + Base + Passport string `json:"passport"` + Password string `json:"password"` + Nickname string `json:"nickname"` + } + data := g.Map{ + "id" : 100, + "uid" : 101, + "passport" : "t1", + "password" : "123456", + "nickname" : "T1", + "create_time" : gtime.Now().String(), + } + result, err := db.Table("user").Filter().Data(data).Insert() + gtest.Assert(err, nil) + n, _ := result.RowsAffected() + gtest.Assert(n, 1) + + one, err := db.Table("user").Where("id=100").One() + gtest.Assert(err, nil) + + user := new(User) + + gtest.Assert(one.ToStruct(user), nil) + gtest.Assert(user.Id, data["id"]) + gtest.Assert(user.Passport, data["passport"]) + gtest.Assert(user.Password, data["password"]) + gtest.Assert(user.Nickname, data["nickname"]) + gtest.Assert(user.CreateTime, data["create_time"]) + + // Delete this test data. + _, err = db.Table("user").Where("id", 100).Delete() + gtest.Assert(err, nil) + }) + +} + + diff --git a/g/net/ghttp/ghttp_request_post.go b/g/net/ghttp/ghttp_request_post.go index f78c3bfbb..8255572c9 100644 --- a/g/net/ghttp/ghttp_request_post.go +++ b/g/net/ghttp/ghttp_request_post.go @@ -142,16 +142,16 @@ func (r *Request) GetPostMap(def...map[string]string) map[string]string { } // 将所有的request参数映射到struct属性上,参数object应当为一个struct对象的指针, mapping为非必需参数,自定义参数与属性的映射关系 -func (r *Request) GetPostToStruct(object interface{}, mapping...map[string]string) error { - tagmap := r.getStructParamsTagMap(object) +func (r *Request) GetPostToStruct(pointer interface{}, mapping...map[string]string) error { + tagMap := r.getStructParamsTagMap(pointer) if len(mapping) > 0 { for k, v := range mapping[0] { - tagmap[k] = v + tagMap[k] = v } } params := make(map[string]interface{}) for k, v := range r.GetPostMap() { params[k] = v } - return gconv.Struct(params, object, tagmap) + return gconv.Struct(params, pointer, tagMap) } \ No newline at end of file diff --git a/g/net/ghttp/ghttp_request_query.go b/g/net/ghttp/ghttp_request_query.go index bfc437aef..0b399ce64 100644 --- a/g/net/ghttp/ghttp_request_query.go +++ b/g/net/ghttp/ghttp_request_query.go @@ -150,8 +150,8 @@ func (r *Request) GetQueryMap(def... map[string]string) map[string]string { } // 将所有的get参数映射到struct属性上,参数object应当为一个struct对象的指针, mapping为非必需参数,自定义参数与属性的映射关系 -func (r *Request) GetQueryToStruct(object interface{}, mapping...map[string]string) error { - tagmap := r.getStructParamsTagMap(object) +func (r *Request) GetQueryToStruct(pointer interface{}, mapping...map[string]string) error { + tagmap := r.getStructParamsTagMap(pointer) if len(mapping) > 0 { for k, v := range mapping[0] { tagmap[k] = v @@ -161,5 +161,5 @@ func (r *Request) GetQueryToStruct(object interface{}, mapping...map[string]stri for k, v := range r.GetQueryMap() { params[k] = v } - return gconv.Struct(params, object, tagmap) + return gconv.Struct(params, pointer, tagmap) } \ No newline at end of file diff --git a/g/net/ghttp/ghttp_request_request.go b/g/net/ghttp/ghttp_request_request.go index f51cd4b79..a836903a3 100644 --- a/g/net/ghttp/ghttp_request_request.go +++ b/g/net/ghttp/ghttp_request_request.go @@ -133,10 +133,10 @@ func (r *Request) GetRequestMap(def...map[string]string) map[string]string { // 将所有的request参数映射到struct属性上,参数object应当为一个struct对象的指针, mapping为非必需参数,自定义参数与属性的映射关系 func (r *Request) GetRequestToStruct(pointer interface{}, mapping...map[string]string) error { - tagmap := r.getStructParamsTagMap(pointer) + tagMap := r.getStructParamsTagMap(pointer) if len(mapping) > 0 { for k, v := range mapping[0] { - tagmap[k] = v + tagMap[k] = v } } params := make(map[string]interface{}) @@ -148,6 +148,6 @@ func (r *Request) GetRequestToStruct(pointer interface{}, mapping...map[string]s params = j.ToMap() } } - return gconv.Struct(params, pointer, tagmap) + return gconv.Struct(params, pointer, tagMap) } diff --git a/g/util/gconv/gconv_map.go b/g/util/gconv/gconv_map.go index 6da49ee48..bc9cca1a5 100644 --- a/g/util/gconv/gconv_map.go +++ b/g/util/gconv/gconv_map.go @@ -7,18 +7,19 @@ package gconv import ( - "github.com/gogf/gf/g/internal/empty" - "github.com/gogf/gf/g/text/gstr" - "reflect" - "strings" + "github.com/gogf/gf/g/internal/empty" + "github.com/gogf/gf/g/text/gstr" + "reflect" + "strings" ) const ( gGCONV_TAG = "gconv" ) -// Map converts any variable to map[string]interface{}. -// If the parameter is not a map type, then the conversion will fail and returns nil. -// If is a struct object, the second parameter specifies the most priority + +// Map converts any variable to map[string]interface{}. +// If the parameter is not a map type, then the conversion will fail and returns nil. +// If is a struct object, the second parameter specifies the most priority // tags that will be detected, otherwise it detects the tags in order of: gconv, json. func Map(value interface{}, tags...string) map[string]interface{} { if value == nil { @@ -27,7 +28,7 @@ func Map(value interface{}, tags...string) map[string]interface{} { if r, ok := value.(map[string]interface{}); ok { return r } else { - // Only assert the common combination type of maps, and finally use reflection. + // Only assert the common combination of types, and finally it uses reflection. m := make(map[string]interface{}) switch value.(type) { case map[interface{}]interface{}: @@ -74,7 +75,6 @@ func Map(value interface{}, tags...string) map[string]interface{} { for k, v := range value.(map[string]float64) { m[k] = v } - case map[int]interface{}: for k, v := range value.(map[int]interface{}) { m[String(k)] = v @@ -161,3 +161,25 @@ func Map(value interface{}, tags...string) map[string]interface{} { return m } } + +// MapDeep do Map function recursively. +// See Map. +func MapDeep(value interface{}, tags...string) map[string]interface{} { + data := Map(value, tags...) + for key, value := range data { + rv := reflect.ValueOf(value) + kind := rv.Kind() + if kind == reflect.Ptr { + rv = rv.Elem() + kind = rv.Kind() + } + switch kind { + case reflect.Struct: + delete(data, key) + for k, v := range MapDeep(value, tags...) { + data[k] = v + } + } + } + return data +} diff --git a/g/util/gconv/gconv_struct.go b/g/util/gconv/gconv_struct.go index 879bf7826..8b1b6a325 100644 --- a/g/util/gconv/gconv_struct.go +++ b/g/util/gconv/gconv_struct.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright 2017-2019 gf Author(https://github.com/gogf/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, @@ -15,35 +15,36 @@ import ( "strings" ) -// Struct maps the params key-value pairs to the corresponding struct object properties. -// The third parameter mapping is unnecessary, indicating the mapping between the custom name -// and the attribute name. +// Struct maps the params key-value pairs to the corresponding struct object's properties. +// The third parameter is unnecessary, indicating the mapping rules between the custom key name +// and the attribute name(case sensitive). // // Note: -// 1. The can be any type of may/struct, usually a map; -// 2. The second parameter should be a pointer to the struct object; -// 3. Only the public attributes of struct object can be mapped; +// 1. The can be any type of map/struct, usually a map. +// 2. The second parameter should be a pointer to the struct object. +// 3. Only the public attributes of struct object can be mapped. // 4. If is a map, the key of the map can be lowercase. // It will automatically convert the first letter of the key to uppercase // in mapping procedure to do the matching. -// If it does not match, ignore the key; -func Struct(params interface{}, objPointer interface{}, attrMapping...map[string]string) error { +// It ignores the map key, if it does not match. +func Struct(params interface{}, pointer interface{}, mapping...map[string]string) error { if params == nil { return errors.New("params cannot be nil") } - if objPointer == nil { + if pointer == nil { return errors.New("object pointer cannot be nil") } paramsMap := Map(params) if paramsMap == nil { return fmt.Errorf("invalid params: %v", params) } - // struct的反射对象 + // Using reflect to do the converting, + // it also supports type of reflect.Value for (always in internal usage). elem := reflect.Value{} - if v, ok := objPointer.(reflect.Value); ok { + if v, ok := pointer.(reflect.Value); ok { elem = v } else { - rv := reflect.ValueOf(objPointer) + rv := reflect.ValueOf(pointer) if kind := rv.Kind(); kind != reflect.Ptr { return fmt.Errorf("object pointer should be type of: %v", kind) } @@ -52,12 +53,12 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string } elem = rv.Elem() } - // 已执行过转换的属性,只执行一次转换。 - // 或者是已经执行过转换检查的属性(即使不进行转换), 以便重复判断。 + // It only performs one converting to the same attribute. + // doneMap is used to check repeated converting. doneMap := make(map[string]bool) - // 首先按照传递的映射关系进行匹配 - if len(attrMapping) > 0 && len(attrMapping[0]) > 0 { - for mapK, mapV := range attrMapping[0] { + // It first checks the passed mapping rules. + if len(mapping) > 0 && len(mapping[0]) > 0 { + for mapK, mapV := range mapping[0] { if v, ok := paramsMap[mapK]; ok { doneMap[mapV] = true if err := bindVarToStructAttr(elem, mapV, v); err != nil { @@ -66,25 +67,24 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string } } } - // 其次匹配对象定义时绑定的属性名称, - // 标签映射关系map,如果有的话 - tagMap := getTagMapOfStruct(objPointer) - for tagk, tagv := range tagMap { - if _, ok := doneMap[tagv]; ok { + // It secondly checks the tags of attributes. + tagMap := getTagMapOfStruct(pointer) + for tagK, tagV := range tagMap { + if _, ok := doneMap[tagV]; ok { continue } - if v, ok := paramsMap[tagk]; ok { - doneMap[tagv] = true - if err := bindVarToStructAttr(elem, tagv, v); err != nil { + if v, ok := paramsMap[tagK]; ok { + doneMap[tagV] = true + if err := bindVarToStructAttr(elem, tagV, v); err != nil { return err } } } - // 最后按照默认规则进行匹配 + // It finally do the converting with default rules. attrMap := make(map[string]struct{}) elemType := elem.Type() for i := 0; i < elem.NumField(); i++ { - // 只转换公开属性 + // Only do converting to public attributes. if !gstr.IsLetterUpper(elemType.Field(i).Name[0]) { continue } @@ -105,7 +105,7 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string if _, ok := tagMap[checkName]; ok { continue } - // 循环查找属性名称进行匹配 + // Loop to find the matched attribute name. for value, _ := range attrMap { if strings.EqualFold(checkName, value) { name = value @@ -121,7 +121,7 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string break } } - // 如果没有匹配到属性名称,放弃 + // No matching, give up this attribute converting. if name == "" { continue } @@ -132,15 +132,49 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string return nil } +// StructDeep do Struct function recursively. +// See Struct. +func StructDeep(params interface{}, pointer interface{}, mapping...map[string]string) error { + 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() + if 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 !gstr.IsLetterUpper(rt.Field(i).Name[0]) { + continue + } + trv := rv.Field(i) + switch trv.Kind() { + case reflect.Struct: + StructDeep(params, trv, mapping...) + } + } + } + } + return nil +} + // 解析指针对象的tag -func getTagMapOfStruct(objPointer interface{}) map[string]string { - tagmap := make(map[string]string) +func getTagMapOfStruct(pointer interface{}) map[string]string { + tagMap := make(map[string]string) // 反射类型判断 fields := ([]*structs.Field)(nil) - if v, ok := objPointer.(reflect.Value); ok { + if v, ok := pointer.(reflect.Value); ok { fields = structs.Fields(v.Interface()) } else { - fields = structs.Fields(objPointer) + fields = structs.Fields(pointer) } // 将struct中定义的属性转换名称构建成tagmap for _, field := range fields { @@ -150,11 +184,11 @@ func getTagMapOfStruct(objPointer interface{}) map[string]string { } if tag != "" { for _, v := range strings.Split(tag, ",") { - tagmap[strings.TrimSpace(v)] = field.Name() + tagMap[strings.TrimSpace(v)] = field.Name() } } } - return tagmap + return tagMap } // 将参数值绑定到对象指定名称的属性上 diff --git a/g/util/gconv/gconv_z_unit_map_test.go b/g/util/gconv/gconv_z_unit_map_test.go index 15d2ac16a..e9b4c3f40 100644 --- a/g/util/gconv/gconv_z_unit_map_test.go +++ b/g/util/gconv/gconv_z_unit_map_test.go @@ -123,23 +123,30 @@ func Test_Map_PrivateAttribute(t *testing.T) { gtest.Assert(gconv.Map(user), g.Map{"Id" : 1}) }) } -// -//func Test_Map_StructInherit(t *testing.T) { -// type Base struct { -// Id int -// } -// type User struct { -// Base -// Name string -// } -// gtest.Case(t, func() { -// user := &User{ -// Base : Base { -// Id : 100, -// }, -// Name : "john", -// } -// fmt.Println(gconv.Map(user)) -// //gtest.Assert(gconv.Map(user), g.Map{"Id" : 1}) -// }) -//} \ No newline at end of file + +func Test_Map_StructInherit(t *testing.T) { + gtest.Case(t, func() { + type Ids struct { + Id int `json:"id"` + Uid int `json:"uid"` + } + type Base struct { + Ids + CreateTime string `json:"create_time"` + } + type User struct { + Base + Passport string `json:"passport"` + Password string `json:"password"` + Nickname string `json:"nickname"` + } + user := new(User) + user.Id = 100 + user.Nickname = "john" + user.CreateTime = "2019" + m := gconv.MapDeep(user) + gtest.Assert(m["id"], user.Id) + gtest.Assert(m["nickname"], user.Nickname) + gtest.Assert(m["create_time"], user.CreateTime) + }) +} \ No newline at end of file diff --git a/g/util/gconv/gconv_z_unit_struct_test.go b/g/util/gconv/gconv_z_unit_struct_test.go index 78af7942c..c2426083a 100644 --- a/g/util/gconv/gconv_z_unit_struct_test.go +++ b/g/util/gconv/gconv_z_unit_struct_test.go @@ -7,10 +7,10 @@ package gconv_test import ( - "github.com/gogf/gf/g" - "github.com/gogf/gf/g/test/gtest" - "github.com/gogf/gf/g/util/gconv" - "testing" + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/test/gtest" + "github.com/gogf/gf/g/util/gconv" + "testing" ) func Test_Struct_Basic1(t *testing.T) { @@ -302,7 +302,6 @@ func Test_Struct_Attr_Struct_Slice_Ptr(t *testing.T) { }) } -// 私有属性不会进行转换 func Test_Struct_PrivateAttribute(t *testing.T) { type User struct { Id int @@ -315,4 +314,37 @@ func Test_Struct_PrivateAttribute(t *testing.T) { gtest.Assert(user.Id, 1) gtest.Assert(user.name, "") }) +} + +func Test_Struct_Deep(t *testing.T) { + gtest.Case(t, func() { + type Ids struct { + Id int `json:"id"` + Uid int `json:"uid"` + } + type Base struct { + Ids + CreateTime string `json:"create_time"` + } + type User struct { + Base + Passport string `json:"passport"` + Password string `json:"password"` + Nickname string `json:"nickname"` + } + data := g.Map{ + "id" : 100, + "uid" : 101, + "passport" : "t1", + "password" : "123456", + "nickname" : "T1", + "create_time" : "2019", + } + user := new(User) + gconv.StructDeep(data, user) + gtest.Assert(user.Id, 100) + gtest.Assert(user.Uid, 101) + gtest.Assert(user.Nickname, "T1") + gtest.Assert(user.CreateTime, "2019") + }) } \ No newline at end of file