Skip to content

Commit

Permalink
improve map converting feature using MapOption for package gconv (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
gqcn committed Nov 23, 2023
1 parent ea5d52c commit 9aa872e
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 50 deletions.
28 changes: 17 additions & 11 deletions container/gvar/gvar_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,27 @@ package gvar

import "github.com/gogf/gf/v2/util/gconv"

// MapOption specifies the option for map converting.
type MapOption = gconv.MapOption

// Map converts and returns `v` as map[string]interface{}.
func (v *Var) Map(tags ...string) map[string]interface{} {
return gconv.Map(v.Val(), tags...)
func (v *Var) Map(option ...MapOption) map[string]interface{} {
return gconv.Map(v.Val(), option...)
}

// MapStrAny is like function Map, but implements the interface of MapStrAny.
func (v *Var) MapStrAny() map[string]interface{} {
return v.Map()
func (v *Var) MapStrAny(option ...MapOption) map[string]interface{} {
return v.Map(option...)
}

// MapStrStr converts and returns `v` as map[string]string.
func (v *Var) MapStrStr(tags ...string) map[string]string {
return gconv.MapStrStr(v.Val(), tags...)
func (v *Var) MapStrStr(option ...MapOption) map[string]string {
return gconv.MapStrStr(v.Val(), option...)
}

// MapStrVar converts and returns `v` as map[string]Var.
func (v *Var) MapStrVar(tags ...string) map[string]*Var {
m := v.Map(tags...)
func (v *Var) MapStrVar(option ...MapOption) map[string]*Var {
m := v.Map(option...)
if len(m) > 0 {
vMap := make(map[string]*Var, len(m))
for k, v := range m {
Expand All @@ -37,16 +40,19 @@ func (v *Var) MapStrVar(tags ...string) map[string]*Var {
}

// MapDeep converts and returns `v` as map[string]interface{} recursively.
// Deprecated: used Map instead.
func (v *Var) MapDeep(tags ...string) map[string]interface{} {
return gconv.MapDeep(v.Val(), tags...)
}

// MapStrStrDeep converts and returns `v` as map[string]string recursively.
// Deprecated: used MapStrStr instead.
func (v *Var) MapStrStrDeep(tags ...string) map[string]string {
return gconv.MapStrStrDeep(v.Val(), tags...)
}

// MapStrVarDeep converts and returns `v` as map[string]*Var recursively.
// Deprecated: used MapStrVar instead.
func (v *Var) MapStrVarDeep(tags ...string) map[string]*Var {
m := v.MapDeep(tags...)
if len(m) > 0 {
Expand All @@ -61,12 +67,12 @@ func (v *Var) MapStrVarDeep(tags ...string) map[string]*Var {

// Maps converts and returns `v` as map[string]string.
// See gconv.Maps.
func (v *Var) Maps(tags ...string) []map[string]interface{} {
return gconv.Maps(v.Val(), tags...)
func (v *Var) Maps(option ...MapOption) []map[string]interface{} {
return gconv.Maps(v.Val(), option...)
}

// MapsDeep converts `value` to []map[string]interface{} recursively.
// See gconv.MapsDeep.
// Deprecated: used Maps instead.
func (v *Var) MapsDeep(tags ...string) []map[string]interface{} {
return gconv.MapsDeep(v.Val(), tags...)
}
Expand Down
4 changes: 2 additions & 2 deletions database/gdb/gdb_func.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,14 +210,14 @@ func GetInsertOperationByOption(option InsertOption) string {
}

func anyValueToMapBeforeToRecord(value interface{}) map[string]interface{} {
return gconv.Map(value, structTagPriority...)
return gconv.Map(value, gconv.MapOption{Tags: structTagPriority})
}

// DataToMapDeep converts `value` to map type recursively(if attribute struct is embedded).
// The parameter `value` should be type of *map/map/*struct/struct.
// It supports embedded struct definition for struct.
func DataToMapDeep(value interface{}) map[string]interface{} {
m := gconv.Map(value, structTagPriority...)
m := gconv.Map(value, gconv.MapOption{Tags: structTagPriority})
for k, v := range m {
switch v.(type) {
case time.Time, *time.Time, gtime.Time, *gtime.Time, gjson.Json, *gjson.Json:
Expand Down
84 changes: 59 additions & 25 deletions util/gconv/gconv_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,40 +22,57 @@ const (
recursiveTypeTrue recursiveType = "true"
)

// MapOption specifies the option for map converting.
type MapOption struct {
// Deep marks doing Map function recursively, which means if the attribute of given converting value
// is also a struct/*struct, it automatically calls Map function on this attribute converting it to
// a map[string]interface{} type variable.
Deep bool

// OmitEmpty ignores the attributes that has json omitempty tag.
OmitEmpty bool

// Tags specifies the converted map key name by struct tag name.
Tags []string
}

// Map converts any variable `value` to map[string]interface{}. If the parameter `value` is not a
// map/struct/*struct type, then the conversion will fail and returns nil.
//
// If `value` is a struct/*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, field name.
func Map(value interface{}, tags ...string) map[string]interface{} {
return doMapConvert(value, recursiveTypeAuto, false, tags...)
func Map(value interface{}, option ...MapOption) map[string]interface{} {
return doMapConvert(value, recursiveTypeAuto, false, option...)
}

// MapDeep does Map function recursively, which means if the attribute of `value`
// is also a struct/*struct, calls Map function on this attribute converting it to
// a map[string]interface{} type variable.
// Also see Map.
// Deprecated: used Map instead.
func MapDeep(value interface{}, tags ...string) map[string]interface{} {
return doMapConvert(value, recursiveTypeTrue, false, tags...)
return doMapConvert(value, recursiveTypeTrue, false, MapOption{
Tags: tags,
})
}

// doMapConvert implements the map converting.
// It automatically checks and converts json string to map if `value` is string/[]byte.
//
// TODO completely implement the recursive converting for all types, especially the map.
func doMapConvert(value interface{}, recursive recursiveType, mustMapReturn bool, tags ...string) map[string]interface{} {
func doMapConvert(value interface{}, recursive recursiveType, mustMapReturn bool, option ...MapOption) map[string]interface{} {
if value == nil {
return nil
}
var usedOption = getUsedMapOption(option...)
newTags := StructTagPriority
switch len(tags) {
switch len(usedOption.Tags) {
case 0:
// No need handling.
case 1:
newTags = append(strings.Split(tags[0], ","), StructTagPriority...)
newTags = append(strings.Split(usedOption.Tags[0], ","), StructTagPriority...)
default:
newTags = append(tags, StructTagPriority...)
newTags = append(usedOption.Tags, StructTagPriority...)
}
// Assert the common combination of types, and finally it uses reflection.
dataMap := make(map[string]interface{})
Expand All @@ -79,14 +96,16 @@ func doMapConvert(value interface{}, recursive recursiveType, mustMapReturn bool
return nil
}
case map[interface{}]interface{}:
recursiveOption := usedOption
recursiveOption.Tags = newTags
for k, v := range r {
dataMap[String(k)] = doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: v,
RecursiveType: recursive,
RecursiveOption: recursive == recursiveTypeTrue,
Tags: newTags,
Option: recursiveOption,
},
)
}
Expand Down Expand Up @@ -136,6 +155,8 @@ func doMapConvert(value interface{}, recursive recursiveType, mustMapReturn bool
}
case map[string]interface{}:
if recursive == recursiveTypeTrue {
recursiveOption := usedOption
recursiveOption.Tags = newTags
// A copy of current map.
for k, v := range r {
dataMap[k] = doMapConvertForMapOrStructValue(
Expand All @@ -144,7 +165,7 @@ func doMapConvert(value interface{}, recursive recursiveType, mustMapReturn bool
Value: v,
RecursiveType: recursive,
RecursiveOption: recursive == recursiveTypeTrue,
Tags: newTags,
Option: recursiveOption,
},
)
}
Expand All @@ -153,14 +174,16 @@ func doMapConvert(value interface{}, recursive recursiveType, mustMapReturn bool
return r
}
case map[int]interface{}:
recursiveOption := usedOption
recursiveOption.Tags = newTags
for k, v := range r {
dataMap[String(k)] = doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: v,
RecursiveType: recursive,
RecursiveOption: recursive == recursiveTypeTrue,
Tags: newTags,
Option: recursiveOption,
},
)
}
Expand Down Expand Up @@ -202,13 +225,15 @@ func doMapConvert(value interface{}, recursive recursiveType, mustMapReturn bool
}
}
case reflect.Map, reflect.Struct, reflect.Interface:
recursiveOption := usedOption
recursiveOption.Tags = newTags
convertedValue := doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: true,
Value: value,
RecursiveType: recursive,
RecursiveOption: recursive == recursiveTypeTrue,
Tags: newTags,
Option: recursiveOption,
MustMapReturn: mustMapReturn,
},
)
Expand All @@ -223,12 +248,20 @@ func doMapConvert(value interface{}, recursive recursiveType, mustMapReturn bool
return dataMap
}

func getUsedMapOption(option ...MapOption) MapOption {
var usedOption MapOption
if len(option) > 0 {
usedOption = option[0]
}
return usedOption
}

type doMapConvertForMapOrStructValueInput struct {
IsRoot bool // It returns directly if it is not root and with no recursive converting.
Value interface{} // Current operation value.
RecursiveType recursiveType // The type from top function entry.
RecursiveOption bool // Whether convert recursively for `current` operation.
Tags []string // Map key mapping.
Option MapOption // Map converting option.
MustMapReturn bool // Must return map instead of Value when empty.
}

Expand Down Expand Up @@ -280,7 +313,7 @@ func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) in
Value: mapValue,
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Tags: in.Tags,
Option: in.Option,
},
)
}
Expand All @@ -299,7 +332,7 @@ func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) in
Value: mapV,
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Tags: in.Tags,
Option: in.Option,
},
)
} else {
Expand Down Expand Up @@ -327,7 +360,7 @@ func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) in
}
mapKey = ""
fieldTag := rtField.Tag
for _, tag := range in.Tags {
for _, tag := range in.Option.Tags {
if mapKey = fieldTag.Get(tag); mapKey != "" {
break
}
Expand All @@ -344,7 +377,7 @@ func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) in
if len(array) > 1 {
switch strings.TrimSpace(array[1]) {
case "omitempty":
if empty.IsEmpty(rvField.Interface()) {
if in.Option.OmitEmpty && empty.IsEmpty(rvField.Interface()) {
continue
} else {
mapKey = strings.TrimSpace(array[0])
Expand Down Expand Up @@ -389,7 +422,7 @@ func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) in
Value: rvInterface,
RecursiveType: in.RecursiveType,
RecursiveOption: true,
Tags: in.Tags,
Option: in.Option,
})
if m, ok := anonymousValue.(map[string]interface{}); ok {
for k, v := range m {
Expand All @@ -406,7 +439,7 @@ func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) in
Value: rvInterface,
RecursiveType: in.RecursiveType,
RecursiveOption: true,
Tags: in.Tags,
Option: in.Option,
})

default:
Expand All @@ -415,7 +448,7 @@ func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) in
Value: rvInterface,
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Tags: in.Tags,
Option: in.Option,
})
}

Expand All @@ -434,7 +467,7 @@ func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) in
Value: rvAttrField.Index(arrayIndex).Interface(),
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Tags: in.Tags,
Option: in.Option,
},
)
}
Expand All @@ -451,7 +484,7 @@ func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) in
Value: rvAttrField.MapIndex(k).Interface(),
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Tags: in.Tags,
Option: in.Option,
},
)
}
Expand Down Expand Up @@ -490,7 +523,7 @@ func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) in
Value: reflectValue.Index(i).Interface(),
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Tags: in.Tags,
Option: in.Option,
})
}
return array
Expand All @@ -500,11 +533,11 @@ func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) in

// MapStrStr converts `value` to map[string]string.
// Note that there might be data copy for this map type converting.
func MapStrStr(value interface{}, tags ...string) map[string]string {
func MapStrStr(value interface{}, option ...MapOption) map[string]string {
if r, ok := value.(map[string]string); ok {
return r
}
m := Map(value, tags...)
m := Map(value, option...)
if len(m) > 0 {
vMap := make(map[string]string, len(m))
for k, v := range m {
Expand All @@ -517,6 +550,7 @@ func MapStrStr(value interface{}, tags ...string) map[string]string {

// MapStrStrDeep converts `value` to map[string]string recursively.
// Note that there might be data copy for this map type converting.
// Deprecated: used MapStrStr instead.
func MapStrStrDeep(value interface{}, tags ...string) map[string]string {
if r, ok := value.(map[string]string); ok {
return r
Expand Down

0 comments on commit 9aa872e

Please sign in to comment.