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 break
} }
// Automatically mapping and filtering the struct attribute. // Automatically mapping and filtering the struct attribute.
// TODO struct fields in sequence
data := DataToMapDeep(in.Where) data := DataToMapDeep(in.Where)
if in.Table != "" { if in.Table != "" {
data, _ = db.GetCore().mappingAndFilterData(in.Schema, in.Table, data, true) data, _ = db.GetCore().mappingAndFilterData(in.Schema, in.Table, data, true)

View File

@ -273,7 +273,7 @@ func (g *RouterGroup) getPrefix() string {
return prefix return prefix
} }
// doBindRoutersToServer does really registering for the group. // doBindRoutersToServer does really register for the group.
func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup { func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup {
var ( var (
bindType = item.bindType bindType = item.bindType
@ -289,7 +289,7 @@ func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup {
if err != nil { if err != nil {
g.server.Logger().Fatalf("invalid pattern: %s", pattern) 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 { if g.domain != nil {
domain = "" 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. // When you add a scheduled task, you cannot allow it to run.
// It cannot start running when added to gtimer. // It cannot start running when added to gtimer.
// It should start running after the entry is added to the entries map, // It should start running after the entry is added to the Cron entries map, to avoid the task
// to avoid the task from running during adding where the entries // from running during adding where the entries do not have the entry information, which might cause panic.
// does not have the entry information, which might cause panic.
entry.entry = gtimer.AddEntry(time.Second, entry.check, singleton, -1, gtimer.StatusStopped) entry.entry = gtimer.AddEntry(time.Second, entry.check, singleton, -1, gtimer.StatusStopped)
c.entries.Set(entry.Name, entry) c.entries.Set(entry.Name, entry)
entry.entry.Start() entry.entry.Start()
@ -70,7 +69,7 @@ func (entry *Entry) IsSingleton() bool {
// SetSingleton sets the entry running in singleton mode. // SetSingleton sets the entry running in singleton mode.
func (entry *Entry) SetSingleton(enabled bool) { func (entry *Entry) SetSingleton(enabled bool) {
entry.entry.SetSingleton(true) entry.entry.SetSingleton(enabled)
} }
// SetTimes sets the times which the entry can run. // 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`. // doMapToMap converts any map type variable `params` to another map type variable `pointer`.
// //
// The parameter `params` can be any type of map, like: // 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: // 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 // 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. // 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 ( var (
paramsRv reflect.Value paramsRv reflect.Value
paramsKind reflect.Kind paramsKind reflect.Kind
keyToAttributeNameMapping map[string]string
) )
if len(mapping) > 0 {
keyToAttributeNameMapping = mapping[0]
}
if v, ok := params.(reflect.Value); ok { if v, ok := params.(reflect.Value); ok {
paramsRv = v paramsRv = v
} else { } else {
@ -113,7 +117,7 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s
e := reflect.New(pointerValueType).Elem() e := reflect.New(pointerValueType).Elem()
switch pointerValueKind { switch pointerValueKind {
case reflect.Map, reflect.Struct: 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 return err
} }
default: default:

View File

@ -11,28 +11,41 @@ import (
"reflect" "reflect"
) )
// Scan automatically calls MapToMap, MapToMaps, Struct or Structs function according to // Scan automatically checks the type of `pointer` and converts `params` to `pointer`. It supports `pointer`
// the type of parameter `pointer` to implement the converting. // 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 `doMapToMap` internally if `pointer` is type of *map for converting.
// It calls function MapToMaps if `pointer` is type of *[]map/*[]*map to do the converting. // It calls function `doMapToMaps` internally if `pointer` is type of *[]map/*[]*map for converting.
// It calls function Struct if `pointer` is type of *struct/**struct to do the converting. // It calls function `doStruct` internally if `pointer` is type of *struct/**struct for converting.
// It calls function Structs if `pointer` is type of *[]struct/*[]*struct to do the 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) { func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
var ( var (
pointerType = reflect.TypeOf(pointer) pointerType reflect.Type
pointerKind = pointerType.Kind() 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 { 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 ( var (
pointerElem = pointerType.Elem() pointerElem = pointerType.Elem()
pointerElemKind = pointerElem.Kind() pointerElemKind = pointerElem.Kind()
keyToAttributeNameMapping map[string]string
) )
if len(mapping) > 0 {
keyToAttributeNameMapping = mapping[0]
}
switch pointerElemKind { switch pointerElemKind {
case reflect.Map: case reflect.Map:
return MapToMap(params, pointer, mapping...) return doMapToMap(params, pointer, mapping...)
case reflect.Array, reflect.Slice: case reflect.Array, reflect.Slice:
var ( var (
@ -44,11 +57,13 @@ func Scan(params interface{}, pointer interface{}, mapping ...map[string]string)
sliceElemKind = sliceElem.Kind() sliceElemKind = sliceElem.Kind()
} }
if sliceElemKind == reflect.Map { 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: 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. // in mapping procedure to do the matching.
// It ignores the map key, if it does not match. // It ignores the map key, if it does not match.
func Struct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { func Struct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
var keyToAttributeNameMapping map[string]string return Scan(params, pointer, mapping...)
if len(mapping) > 0 {
keyToAttributeNameMapping = mapping[0]
}
return doStruct(params, pointer, keyToAttributeNameMapping, "")
} }
// StructTag acts as Struct but also with support for priority tag feature, which retrieves the // 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. // Converting by kind.
switch kind { switch kind {
case reflect.Map:
return doMapToMap(value, structFieldValue, mapping)
case reflect.Struct: case reflect.Struct:
// Recursively converting for struct attribute. // Recursively converting for struct attribute.
if err := doStruct(value, structFieldValue, nil, ""); err != nil { if err := doStruct(value, structFieldValue, nil, ""); err != nil {

View File

@ -13,12 +13,9 @@ import (
) )
// Structs converts any slice to given struct slice. // Structs converts any slice to given struct slice.
// Also see Scan, Struct.
func Structs(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { func Structs(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
var keyToAttributeNameMapping map[string]string return Scan(params, pointer, mapping...)
if len(mapping) > 0 {
keyToAttributeNameMapping = mapping[0]
}
return doStructs(params, pointer, keyToAttributeNameMapping, "")
} }
// StructsTag acts as Structs but also with support for priority tag feature, which retrieves the // 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) { gtest.C(t, func(t *gtest.T) {
m := make(map[string]User) m := make(map[string]User)
err := gconv.MapToMap(params, &m) err := gconv.MapToMap(params, &m)
t.Assert(err, nil) t.AssertNil(err)
t.Assert(len(m), 1) t.Assert(len(m), 1)
t.Assert(m["key"].Id, 1) t.Assert(m["key"].Id, 1)
t.Assert(m["key"].Name, "john") 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)
})
}