mirror of
https://gitee.com/johng/gf
synced 2026-06-07 02:12:11 +08:00
add gconv.StructDeep/MapDeep functions for gconv; add struct inherit converting support for gdb
This commit is contained in:
@ -80,9 +80,9 @@ func (v *Var) GTime(format...string) *gtime.Time {
|
||||
|
||||
// Struct maps value of <v> to <objPointer>.
|
||||
// The param <objPointer> should be a pointer to a struct instance.
|
||||
// The param <attrMapping> 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 <mapping> 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 }
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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对象上,注意参数<pointer>是一个指向struct的指针。
|
||||
func mapToStruct(data map[string]interface{}, pointer interface{}) error {
|
||||
return gconv.StructDeep(data, pointer)
|
||||
}
|
||||
@ -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]
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
99
g/database/gdb/gdb_unit_struct_inherit_test.go
Normal file
99
g/database/gdb/gdb_unit_struct_inherit_test.go
Normal file
@ -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)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
@ -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 <i> to map[string]interface{}.
|
||||
// If the parameter <i> is not a map type, then the conversion will fail and returns nil.
|
||||
// If <i> is a struct object, the second parameter <tags> specifies the most priority
|
||||
|
||||
// Map converts any variable <value> to map[string]interface{}.
|
||||
// If the parameter <value> is not a map type, then the conversion will fail and returns nil.
|
||||
// If <value> is a struct object, the second parameter <tags> 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
|
||||
}
|
||||
|
||||
@ -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 <mapping> is unnecessary, indicating the mapping rules between the custom key name
|
||||
// and the attribute name(case sensitive).
|
||||
//
|
||||
// Note:
|
||||
// 1. The <params> can be any type of may/struct, usually a map;
|
||||
// 2. The second parameter <objPointer> should be a pointer to the struct object;
|
||||
// 3. Only the public attributes of struct object can be mapped;
|
||||
// 1. The <params> can be any type of map/struct, usually a map.
|
||||
// 2. The second parameter <pointer> should be a pointer to the struct object.
|
||||
// 3. Only the public attributes of struct object can be mapped.
|
||||
// 4. If <params> is a map, the key of the map <params> 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 <pointer>(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
|
||||
}
|
||||
|
||||
// 将参数值绑定到对象指定名称的属性上
|
||||
|
||||
@ -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})
|
||||
// })
|
||||
//}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
@ -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")
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user