mirror of
https://github.com/charlienet/go-mixed.git
synced 2025-07-18 00:22:41 +08:00
结构体转map
This commit is contained in:
7
structs/copy.go
Normal file
7
structs/copy.go
Normal 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
12
structs/copy_test.go
Normal 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
35
structs/struct_to_map.go
Normal 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
|
||||||
|
}
|
25
structs/struct_to_map_test.go
Normal file
25
structs/struct_to_map_test.go
Normal 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
60
structs/tags.go
Normal 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
45
structs/utils.go
Normal 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()
|
||||||
|
}
|
||||||
|
|
Reference in New Issue
Block a user