1
0
mirror of https://github.com/charlienet/go-mixed.git synced 2025-07-18 00:22:41 +08:00

结构体转map

This commit is contained in:
2022-04-22 14:30:37 +08:00
parent 2771310fe2
commit efc8210d97
6 changed files with 184 additions and 0 deletions

7
structs/copy.go Normal file
View File

@ -0,0 +1,7 @@
package structs
func Copy(source, target any, opts ...optionFunc) {
opt := createOptions(opts)
_ = opt
}

12
structs/copy_test.go Normal file
View File

@ -0,0 +1,12 @@
package structs
import "testing"
func TestCopy(t *testing.T) {
v1 := struct{ Abc string }{Abc: "abc"}
v2 := struct{ Abc string }{}
Copy(v1, v2, IgnoreEmpty())
t.Log(v2)
}

35
structs/struct_to_map.go Normal file
View File

@ -0,0 +1,35 @@
package structs
import (
"reflect"
)
const tagName = "json"
func ToMap(o any, opts ...optionFunc) map[string]any {
typ := reflect.TypeOf(o)
kind := typ.Kind()
if kind == reflect.Map {
if h, ok := o.(map[string]any); ok {
return h
}
}
opt := createOptions(opts)
val := reflect.ValueOf(o)
m := make(map[string]any)
for i := 0; i < val.NumField(); i++ {
fi := typ.Field(i)
field := getFieldOption(fi)
source := val.FieldByName(fi.Name)
if shouldIgnore(source, opt.IgnoreEmpty || field.omitEmpty && opt.Omitempty) {
continue
}
m[field.name] = source.Interface()
}
return m
}

View File

@ -0,0 +1,25 @@
package structs_test
import (
"testing"
"github.com/charlienet/go-mixed/structs"
)
func TestStructToMap(t *testing.T) {
o := struct {
Abc string
InTagName string `json:"in_tag_name,omitempty"`
KeepEmpty int
OmitEmpty int `json:",omitempty"`
}{
Abc: "测试字段",
InTagName: "具体名称",
KeepEmpty: 0,
OmitEmpty: 0,
}
t.Log(structs.ToMap(o))
t.Log(structs.ToMap(o, structs.IgnoreEmpty()))
t.Log(structs.ToMap(o, structs.Omitempty()))
}

60
structs/tags.go Normal file
View File

@ -0,0 +1,60 @@
package structs
import (
"reflect"
"strings"
"unicode"
"github.com/charlienet/go-mixed/expr"
)
type tagOptions string
func parseTag(tag string) (string, tagOptions) {
tag, opt, _ := strings.Cut(tag, ",")
return tag, tagOptions(opt)
}
func (o tagOptions) Contains(optionName string) bool {
if len(o) == 0 {
return false
}
s := string(o)
for s != "" {
var name string
name, s, _ = strings.Cut(s, ",")
if name == optionName {
return true
}
}
return false
}
type fieldOption struct {
name string
omitEmpty bool
}
func getFieldOption(fi reflect.StructField) fieldOption {
name, opts := parseTag(fi.Tag.Get(tagName))
return fieldOption{
name: expr.If(isValidTag(name), name, fi.Name),
omitEmpty: opts.Contains("omitempty"),
}
}
func isValidTag(s string) bool {
if s == "" {
return false
}
for _, c := range s {
switch {
case strings.ContainsRune("!#$%&()*+-./:;<=>?@[]^_{|}~ ", c):
case !unicode.IsLetter(c) && !unicode.IsDigit(c):
return false
}
}
return true
}

45
structs/utils.go Normal file
View File

@ -0,0 +1,45 @@
package structs
import (
"reflect"
)
type optionFunc func(*option)
type option struct {
IgnoreEmpty bool
DeepCopy bool
Omitempty bool
}
func IgnoreEmpty() optionFunc {
return func(o *option) {
o.IgnoreEmpty = true
}
}
func DeepCopy() optionFunc {
return func(o *option) {
o.DeepCopy = true
}
}
func Omitempty() optionFunc {
return func(o *option) {
o.Omitempty = true
}
}
func createOptions(opts []optionFunc) *option {
o := &option{}
for _, f := range opts {
f(o)
}
return o
}
func shouldIgnore(v reflect.Value, ignoreEmpty bool) bool {
return ignoreEmpty && v.IsZero()
}