improve gconv.Struct for map attribute converting

This commit is contained in:
John Guo 2021-08-19 11:28:25 +08:00
parent 457d552fa0
commit f03f56ba4e
9 changed files with 77 additions and 37 deletions

View File

@ -548,6 +548,7 @@ func formatWhere(db DB, in formatWhereInput) (newWhere string, newArgs []interfa
break
}
// Automatically mapping and filtering the struct attribute.
// TODO struct fields in sequence
data := DataToMapDeep(in.Where)
if in.Table != "" {
data, _ = db.GetCore().mappingAndFilterData(in.Schema, in.Table, data, true)

View File

@ -273,7 +273,7 @@ func (g *RouterGroup) getPrefix() string {
return prefix
}
// doBindRoutersToServer does really registering for the group.
// doBindRoutersToServer does really register for the group.
func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup {
var (
bindType = item.bindType
@ -289,7 +289,7 @@ func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup {
if err != nil {
g.server.Logger().Fatalf("invalid pattern: %s", pattern)
}
// If there'a already a domain, unset the domain field in the pattern.
// If there is already a domain, unset the domain field in the pattern.
if g.domain != nil {
domain = ""
}

View File

@ -54,9 +54,8 @@ func (c *Cron) addEntry(pattern string, job func(), singleton bool, name ...stri
}
// When you add a scheduled task, you cannot allow it to run.
// It cannot start running when added to gtimer.
// It should start running after the entry is added to the entries map,
// to avoid the task from running during adding where the entries
// does not have the entry information, which might cause panic.
// It should start running after the entry is added to the Cron entries map, to avoid the task
// from running during adding where the entries do not have the entry information, which might cause panic.
entry.entry = gtimer.AddEntry(time.Second, entry.check, singleton, -1, gtimer.StatusStopped)
c.entries.Set(entry.Name, entry)
entry.entry.Start()
@ -70,7 +69,7 @@ func (entry *Entry) IsSingleton() bool {
// SetSingleton sets the entry running in singleton mode.
func (entry *Entry) SetSingleton(enabled bool) {
entry.entry.SetSingleton(true)
entry.entry.SetSingleton(enabled)
}
// SetTimes sets the times which the entry can run.

View File

@ -22,10 +22,10 @@ func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]str
// doMapToMap converts any map type variable `params` to another map type variable `pointer`.
//
// The parameter `params` can be any type of map, like:
// map[string]string, map[string]struct, , map[string]*struct, etc.
// map[string]string, map[string]struct, map[string]*struct, etc.
//
// The parameter `pointer` should be type of *map, like:
// map[int]string, map[string]struct, , map[string]*struct, etc.
// map[int]string, map[string]struct, map[string]*struct, etc.
//
// The optional parameter `mapping` is used for struct attribute to map key mapping, which makes
// sense only if the items of original map `params` is type struct.
@ -54,9 +54,13 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s
}
}
var (
paramsRv reflect.Value
paramsKind reflect.Kind
paramsRv reflect.Value
paramsKind reflect.Kind
keyToAttributeNameMapping map[string]string
)
if len(mapping) > 0 {
keyToAttributeNameMapping = mapping[0]
}
if v, ok := params.(reflect.Value); ok {
paramsRv = v
} else {
@ -113,7 +117,7 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s
e := reflect.New(pointerValueType).Elem()
switch pointerValueKind {
case reflect.Map, reflect.Struct:
if err = Struct(paramsRv.MapIndex(key).Interface(), e, mapping...); err != nil {
if err = doStruct(paramsRv.MapIndex(key).Interface(), e, keyToAttributeNameMapping, ""); err != nil {
return err
}
default:

View File

@ -11,28 +11,41 @@ import (
"reflect"
)
// Scan automatically calls MapToMap, MapToMaps, Struct or Structs function according to
// the type of parameter `pointer` to implement the converting.
// Scan automatically checks the type of `pointer` and converts `params` to `pointer`. It supports `pointer`
// with type of `*map/*[]map/*[]*map/*struct/**struct/*[]struct/*[]*struct` for converting.
//
// It calls function MapToMap if `pointer` is type of *map to do the converting.
// It calls function MapToMaps if `pointer` is type of *[]map/*[]*map to do the converting.
// It calls function Struct if `pointer` is type of *struct/**struct to do the converting.
// It calls function Structs if `pointer` is type of *[]struct/*[]*struct to do the converting.
// It calls function `doMapToMap` internally if `pointer` is type of *map for converting.
// It calls function `doMapToMaps` internally if `pointer` is type of *[]map/*[]*map for converting.
// It calls function `doStruct` internally if `pointer` is type of *struct/**struct for converting.
// It calls function `doStructs` internally if `pointer` is type of *[]struct/*[]*struct for converting.
func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
var (
pointerType = reflect.TypeOf(pointer)
pointerKind = pointerType.Kind()
pointerType reflect.Type
pointerKind reflect.Kind
)
if v, ok := pointer.(reflect.Value); ok {
pointerType = v.Type()
} else {
pointerType = reflect.TypeOf(pointer)
}
if pointerType == nil {
return gerror.NewCode(gerror.CodeInvalidParameter, "parameter pointer should not be nil")
}
pointerKind = pointerType.Kind()
if pointerKind != reflect.Ptr {
return gerror.NewCodef(gerror.CodeInvalidParameter, "params should be type of pointer, but got: %v", pointerKind)
return gerror.NewCodef(gerror.CodeInvalidParameter, "params should be type of pointer, but got type: %v", pointerKind)
}
var (
pointerElem = pointerType.Elem()
pointerElemKind = pointerElem.Kind()
pointerElem = pointerType.Elem()
pointerElemKind = pointerElem.Kind()
keyToAttributeNameMapping map[string]string
)
if len(mapping) > 0 {
keyToAttributeNameMapping = mapping[0]
}
switch pointerElemKind {
case reflect.Map:
return MapToMap(params, pointer, mapping...)
return doMapToMap(params, pointer, mapping...)
case reflect.Array, reflect.Slice:
var (
@ -44,11 +57,13 @@ func Scan(params interface{}, pointer interface{}, mapping ...map[string]string)
sliceElemKind = sliceElem.Kind()
}
if sliceElemKind == reflect.Map {
return MapToMaps(params, pointer, mapping...)
return doMapToMaps(params, pointer, mapping...)
}
return Structs(params, pointer, mapping...)
return doStructs(params, pointer, keyToAttributeNameMapping, "")
default:
return Struct(params, pointer, mapping...)
return doStruct(params, pointer, keyToAttributeNameMapping, "")
}
}

View File

@ -31,11 +31,7 @@ import (
// 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) {
var keyToAttributeNameMapping map[string]string
if len(mapping) > 0 {
keyToAttributeNameMapping = mapping[0]
}
return doStruct(params, pointer, keyToAttributeNameMapping, "")
return Scan(params, pointer, mapping...)
}
// StructTag acts as Struct but also with support for priority tag feature, which retrieves the
@ -405,6 +401,9 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma
// Converting by kind.
switch kind {
case reflect.Map:
return doMapToMap(value, structFieldValue, mapping)
case reflect.Struct:
// Recursively converting for struct attribute.
if err := doStruct(value, structFieldValue, nil, ""); err != nil {

View File

@ -13,12 +13,9 @@ import (
)
// Structs converts any slice to given struct slice.
// Also see Scan, Struct.
func Structs(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
var keyToAttributeNameMapping map[string]string
if len(mapping) > 0 {
keyToAttributeNameMapping = mapping[0]
}
return doStructs(params, pointer, keyToAttributeNameMapping, "")
return Scan(params, pointer, mapping...)
}
// StructsTag acts as Structs but also with support for priority tag feature, which retrieves the

View File

@ -83,7 +83,7 @@ func Test_MapToMap2(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := make(map[string]User)
err := gconv.MapToMap(params, &m)
t.Assert(err, nil)
t.AssertNil(err)
t.Assert(len(m), 1)
t.Assert(m["key"].Id, 1)
t.Assert(m["key"].Name, "john")

View File

@ -1203,3 +1203,28 @@ func Test_Struct_GVarAttribute(t *testing.T) {
})
}
func Test_Struct_MapAttribute(t *testing.T) {
type NodeStatus struct {
ID int
}
type Nodes map[string]NodeStatus
type Output struct {
Nodes Nodes
}
gtest.C(t, func(t *gtest.T) {
var (
out = Output{}
data = g.Map{
"nodes": g.Map{
"name": g.Map{
"id": 10000,
},
},
}
)
err := gconv.Struct(data, &out)
t.AssertNil(err)
})
}