Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve map converting feature using MapOption for package gconv #3170

Merged
merged 1 commit into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 17 additions & 11 deletions container/gvar/gvar_map.go
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
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
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