prepare for v2
This commit is contained in:
620
copier.go
620
copier.go
@@ -1,623 +1,13 @@
|
|||||||
package copier
|
package copier
|
||||||
|
|
||||||
import (
|
type deepCopier struct {
|
||||||
"fmt"
|
}
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/modern-go/reflect2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Copy(dst, src any, opts ...option) error {
|
func Copy(dst, src any, opts ...option) error {
|
||||||
opt := getOpt(opts...)
|
copier := &deepCopier{}
|
||||||
return copy(dst, src, opt)
|
return copier.Copy(dst, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
func copy(dst, src any, opt *options) error {
|
func (d *deepCopier) Copy(src, dst any) error {
|
||||||
var (
|
|
||||||
srcValue = indirect(reflect.ValueOf(src))
|
|
||||||
dstValue = indirect(reflect.ValueOf(dst))
|
|
||||||
)
|
|
||||||
|
|
||||||
if !srcValue.IsValid() {
|
|
||||||
return ErrInvalidCopyFrom
|
|
||||||
}
|
|
||||||
|
|
||||||
if !dstValue.CanSet() {
|
|
||||||
return ErrInvalidCopyDestination
|
|
||||||
}
|
|
||||||
|
|
||||||
return deepCopy(dstValue, srcValue, 0, "", opt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func deepCopy(dst, src reflect.Value, depth int, fieldName string, opt *options) error {
|
|
||||||
if src.IsZero() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt.ExceedMaxDepth(depth) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// fmt.Println("do deep copy src:", src.Kind().String(), "dst:", dst.Kind().String())
|
|
||||||
switch src.Kind() {
|
|
||||||
case reflect.Slice, reflect.Array:
|
|
||||||
// slice -> slice
|
|
||||||
return copySlice(dst, src, depth, fieldName, opt)
|
|
||||||
case reflect.Map:
|
|
||||||
switch dst.Kind() {
|
|
||||||
case reflect.Map:
|
|
||||||
// map -> map
|
|
||||||
return copyMap(dst, src, depth, fieldName, opt)
|
|
||||||
case reflect.Struct:
|
|
||||||
// map -> struct
|
|
||||||
return copyMap2Struct(dst, src, depth, fieldName, opt)
|
|
||||||
case reflect.Pointer:
|
|
||||||
elemType := dst.Type().Elem()
|
|
||||||
newPtr := reflect.New(elemType)
|
|
||||||
|
|
||||||
if err := deepCopy(newPtr.Elem(), src, depth+1, fieldName, opt); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
dst.Set(newPtr)
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
return ErrNotSupported
|
|
||||||
}
|
|
||||||
case reflect.Struct:
|
|
||||||
switch dst.Kind() {
|
|
||||||
case reflect.Map:
|
|
||||||
// struct -> map
|
|
||||||
return copyStruct2Map(dst, src, depth, fieldName, opt)
|
|
||||||
case reflect.Struct:
|
|
||||||
// struct -> struct
|
|
||||||
return copyStruct(dst, src, depth, opt)
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
if dst.IsNil() {
|
|
||||||
dstType := dst.Type().Elem()
|
|
||||||
newDst := reflect.MakeSlice(reflect.SliceOf(dstType), 1, 1)
|
|
||||||
dst.Set(newDst)
|
|
||||||
|
|
||||||
if err := deepCopy(dst.Index(0), src, depth+1, fieldName, opt); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
len := dst.Len()
|
|
||||||
cap := dst.Cap()
|
|
||||||
newSlice := reflect.MakeSlice(reflect.SliceOf(dst.Type().Elem()), len+1, cap+10)
|
|
||||||
reflect.Copy(newSlice, dst)
|
|
||||||
|
|
||||||
newDst := newSlice.Index(len)
|
|
||||||
if err := deepCopy(newDst, src, depth+1, fieldName, opt); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dst.Set(newSlice)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
return set(dst, src, fieldName, opt)
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Func:
|
|
||||||
if dst.Kind() == reflect.Func && dst.IsNil() {
|
|
||||||
dst.Set(src)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
case reflect.Pointer:
|
|
||||||
if dst.Kind() == reflect.Pointer && dst.IsNil() {
|
|
||||||
if !dst.CanSet() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
p := reflect.New(dst.Type().Elem())
|
|
||||||
dst.Set(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
if src.Kind() == reflect.Pointer {
|
|
||||||
src = indirect(src)
|
|
||||||
}
|
|
||||||
|
|
||||||
if dst.Kind() == reflect.Pointer {
|
|
||||||
dst = dst.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
return deepCopy(dst, src, depth, fieldName, opt)
|
|
||||||
|
|
||||||
case reflect.Interface:
|
|
||||||
if src.IsNil() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if src.Kind() != dst.Kind() {
|
|
||||||
return set(dst, src, fieldName, opt)
|
|
||||||
}
|
|
||||||
|
|
||||||
src = src.Elem()
|
|
||||||
newDst := reflect.New(src.Type().Elem())
|
|
||||||
|
|
||||||
if err := deepCopy(newDst, src, depth, fieldName, opt); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
dst.Set(newDst)
|
|
||||||
return nil
|
|
||||||
case reflect.Chan:
|
|
||||||
return ErrNotSupported
|
|
||||||
default:
|
|
||||||
return set(dst, src, fieldName, opt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyStruct2Map(dst, src reflect.Value, depth int, fieldName string, opt *options) error {
|
|
||||||
for i, n := 0, src.NumField(); i < n; i++ {
|
|
||||||
sf := src.Type().Field(i)
|
|
||||||
if sf.PkgPath != "" && !sf.Anonymous {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if sf.Anonymous {
|
|
||||||
switch sf.Type.Kind() {
|
|
||||||
case reflect.Struct, reflect.Pointer:
|
|
||||||
if err := deepCopy(dst, src.Field(i), depth+1, fieldName, opt); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
tag := parseTag(sf.Tag.Get(opt.tagName))
|
|
||||||
if tag.Contains(tagIgnore) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
name := getFieldName(sf.Name, tag, opt)
|
|
||||||
|
|
||||||
sField := src.Field(i)
|
|
||||||
|
|
||||||
if opt.ignoreEmpty && sField.IsZero() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if sField.Kind() == reflect.Pointer || sField.Kind() == reflect.Struct {
|
|
||||||
var newDst = reflect.ValueOf(make(map[string]any))
|
|
||||||
sField = indirect(sField)
|
|
||||||
|
|
||||||
if err := deepCopy(newDst, sField, depth+1, name, opt); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
dst.SetMapIndex(reflect.ValueOf(name), newDst)
|
|
||||||
} else {
|
|
||||||
dst.SetMapIndex(reflect.ValueOf(name), sField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyMap2Struct(dst, src reflect.Value, depth int, _ string, opt *options) error {
|
|
||||||
// 循环结构,然后从map取值
|
|
||||||
typ := dst.Type()
|
|
||||||
|
|
||||||
for i, n := 0, src.NumField(); i < n; i++ {
|
|
||||||
field := dst.Field(i)
|
|
||||||
sf := typ.Field(i)
|
|
||||||
|
|
||||||
if !field.CanSet() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if sf.Anonymous {
|
|
||||||
if err := deepCopy(field, src, depth+1, sf.Name, opt); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
name := getFieldNameFromJsonTag(sf)
|
|
||||||
if name == "-" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
mapValue := src.MapIndex(reflect.ValueOf(name))
|
|
||||||
if !mapValue.IsValid() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if mapValue.Kind() == reflect.Interface {
|
|
||||||
mapValue = mapValue.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
if mapValue.Kind() == reflect.Map || mapValue.Kind() == reflect.Array || mapValue.Kind() == reflect.Slice {
|
|
||||||
if err := deepCopy(field, mapValue, depth+1, name, opt); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := set(field, mapValue, name, opt); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyMap(dst, src reflect.Value, depth int, fieldName string, opt *options) error {
|
|
||||||
if dst.IsNil() {
|
|
||||||
dst.Set(reflect.MakeMapWithSize(dst.Type(), src.Len()))
|
|
||||||
}
|
|
||||||
|
|
||||||
dstType, _ := indirectType(dst.Type())
|
|
||||||
|
|
||||||
iter := src.MapRange()
|
|
||||||
for iter.Next() {
|
|
||||||
key := iter.Key()
|
|
||||||
value := iter.Value()
|
|
||||||
|
|
||||||
if name, ok := key.Interface().(string); ok {
|
|
||||||
fieldName = opt.NameConvert(name)
|
|
||||||
key = reflect.ValueOf(fieldName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt.ignoreEmpty && value.IsZero() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var copitedValue reflect.Value
|
|
||||||
switch dstType.Elem().Kind() {
|
|
||||||
case reflect.Interface:
|
|
||||||
switch value.Kind() {
|
|
||||||
case reflect.Interface:
|
|
||||||
copitedValue = reflect.New(value.Elem().Type()).Elem()
|
|
||||||
default:
|
|
||||||
copitedValue = reflect.New(value.Type()).Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
set(copitedValue, value, fieldName, opt)
|
|
||||||
default:
|
|
||||||
copitedValue = reflect.New(dstType.Elem()).Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := deepCopy(copitedValue, value, depth+1, fieldName, opt); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
dst.SetMapIndex(key, copitedValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func copySlice(dst, src reflect.Value, depth int, fieldName string, opt *options) error {
|
|
||||||
len := src.Len()
|
|
||||||
if dst.Len() > 0 && dst.Len() < src.Len() {
|
|
||||||
len = dst.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
if dst.Kind() == reflect.Slice && dst.Len() == 0 && src.Len() > 0 {
|
|
||||||
dstType := dst.Type().Elem()
|
|
||||||
newDst := reflect.MakeSlice(reflect.SliceOf(dstType), len, len)
|
|
||||||
dst.Set(newDst)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len; i++ {
|
|
||||||
if err := deepCopy(dst.Index(i), src.Index(i), depth, fieldName, opt); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyStruct(dst, src reflect.Value, depth int, opt *options) error {
|
|
||||||
if dst.CanSet() {
|
|
||||||
if _, ok := src.Interface().(time.Time); ok {
|
|
||||||
dst.Set(src)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typ := src.Type()
|
|
||||||
fields := deepFields(typ)
|
|
||||||
for _, sf := range fields {
|
|
||||||
tag := parseTag(sf.Tag.Get(opt.tagName))
|
|
||||||
if tag.Contains(tagIgnore) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
name := getFieldName(sf.Name, tag, opt)
|
|
||||||
|
|
||||||
if nestedAnonymousField(dst, name) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
typ := reflect2.TypeOf(dst)
|
|
||||||
dstPtr := reflect2.PtrOf(dst)
|
|
||||||
typ.(reflect2.StructType).Field(1).UnsafeGet(dstPtr)
|
|
||||||
|
|
||||||
dstValue := fieldByName(dst, name, opt)
|
|
||||||
if !dstValue.IsValid() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
sField := src.FieldByName(sf.Name)
|
|
||||||
if opt.ignoreEmpty && sField.IsZero() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := deepCopy(dstValue, sField, depth+1, name, opt); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func set(dst, src reflect.Value, fieldName string, opt *options) error {
|
|
||||||
if !src.IsValid() {
|
|
||||||
return ErrInvalidCopyFrom
|
|
||||||
}
|
|
||||||
|
|
||||||
if src.Kind() == reflect.Interface {
|
|
||||||
src = src.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok, err := lookupAndCopyWithConverter(dst, src, fieldName, opt); err != nil {
|
|
||||||
return err
|
|
||||||
} else if ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if dst.Kind() == reflect.Pointer {
|
|
||||||
if dst.IsNil() {
|
|
||||||
if !dst.CanSet() {
|
|
||||||
return ErrInvalidCopyDestination
|
|
||||||
}
|
|
||||||
dst.Set(reflect.New(dst.Type().Elem()))
|
|
||||||
}
|
|
||||||
|
|
||||||
dst = dst.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
if src.Type().AssignableTo(dst.Type()) {
|
|
||||||
dst.Set(src)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if src.Type().ConvertibleTo(dst.Type()) {
|
|
||||||
dst.Set(src.Convert(dst.Type()))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := dst.Interface().(time.Time); ok {
|
|
||||||
switch v := src.Interface().(type) {
|
|
||||||
case string:
|
|
||||||
if t, err := time.Parse(time.RFC3339, v); err == nil {
|
|
||||||
dst.Set(reflect.ValueOf(t))
|
|
||||||
}
|
|
||||||
case int64:
|
|
||||||
dst.Set(reflect.ValueOf(time.Unix(v, 0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch dst.Kind() {
|
|
||||||
case reflect.String:
|
|
||||||
switch v := src.Interface().(type) {
|
|
||||||
case string:
|
|
||||||
dst.SetString(v)
|
|
||||||
case []byte:
|
|
||||||
dst.SetString(string(v))
|
|
||||||
case time.Time:
|
|
||||||
dst.SetString(v.Format(time.RFC3339))
|
|
||||||
default:
|
|
||||||
dst.SetString(fmt.Sprintf("%v", v))
|
|
||||||
}
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
switch v := src.Interface().(type) {
|
|
||||||
case int:
|
|
||||||
dst.SetInt(int64(v))
|
|
||||||
case int8:
|
|
||||||
dst.SetInt(int64(v))
|
|
||||||
case int16:
|
|
||||||
dst.SetInt(int64(v))
|
|
||||||
case int32:
|
|
||||||
dst.SetInt(int64(v))
|
|
||||||
case int64:
|
|
||||||
dst.SetInt(v)
|
|
||||||
case time.Time:
|
|
||||||
dst.SetInt(v.Unix())
|
|
||||||
}
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
switch v := src.Interface().(type) {
|
|
||||||
case uint:
|
|
||||||
dst.SetUint(uint64(v))
|
|
||||||
case uint8:
|
|
||||||
dst.SetUint(uint64(v))
|
|
||||||
case uint16:
|
|
||||||
dst.SetUint(uint64(v))
|
|
||||||
case uint32:
|
|
||||||
dst.SetUint(uint64(v))
|
|
||||||
case uint64:
|
|
||||||
dst.SetUint(v)
|
|
||||||
}
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
switch v := src.Interface().(type) {
|
|
||||||
case float32:
|
|
||||||
dst.SetFloat(float64(v))
|
|
||||||
case float64:
|
|
||||||
dst.SetFloat(v)
|
|
||||||
}
|
|
||||||
case reflect.Bool:
|
|
||||||
switch v := src.Interface().(type) {
|
|
||||||
case bool:
|
|
||||||
dst.SetBool(v)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return ErrNotSupported
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type fieldsWrapper struct {
|
|
||||||
Fields []reflect.StructField
|
|
||||||
once sync.Once
|
|
||||||
}
|
|
||||||
|
|
||||||
var deepFieldsMap sync.Map
|
|
||||||
|
|
||||||
func deepFields(reflectType reflect.Type) []reflect.StructField {
|
|
||||||
if wrapper, ok := deepFieldsMap.Load(reflectType); ok {
|
|
||||||
w := wrapper.(*fieldsWrapper)
|
|
||||||
w.once.Do(func() {
|
|
||||||
w.Fields = calculateDeepFields(reflectType)
|
|
||||||
})
|
|
||||||
return w.Fields
|
|
||||||
}
|
|
||||||
|
|
||||||
wrapper, loaded := deepFieldsMap.LoadOrStore(reflectType, &fieldsWrapper{})
|
|
||||||
w := wrapper.(*fieldsWrapper)
|
|
||||||
if !loaded {
|
|
||||||
w.once.Do(func() {
|
|
||||||
w.Fields = calculateDeepFields(reflectType)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
w.once.Do(func() {})
|
|
||||||
}
|
|
||||||
|
|
||||||
return w.Fields
|
|
||||||
}
|
|
||||||
|
|
||||||
func calculateDeepFields(reflectType reflect.Type) []reflect.StructField {
|
|
||||||
reflectType, _ = indirectType(reflectType)
|
|
||||||
num := reflectType.NumField()
|
|
||||||
res := make([]reflect.StructField, 0, num)
|
|
||||||
if reflectType.Kind() == reflect.Struct {
|
|
||||||
for i := range num {
|
|
||||||
sf := reflectType.Field(i)
|
|
||||||
if sf.PkgPath != "" && !sf.Anonymous {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if sf.Anonymous {
|
|
||||||
res = append(res, deepFields(sf.Type)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
res = append(res, sf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func nestedAnonymousField(dst reflect.Value, fieldName string) bool {
|
|
||||||
if f, ok := dst.Type().FieldByName(fieldName); ok {
|
|
||||||
if len(f.Index) > 1 {
|
|
||||||
destField := dst.Field(f.Index[0])
|
|
||||||
|
|
||||||
if destField.Kind() != reflect.Pointer {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !destField.IsNil() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !destField.CanSet() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
newValue := reflect.New(destField.Type().Elem())
|
|
||||||
destField.Set(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupAndCopyWithConverter(dst, src reflect.Value, fieldName string, opt *options) (bool, error) {
|
|
||||||
if cnv, ok := opt.convertByName[fieldName]; ok {
|
|
||||||
return convert(dst, src, cnv)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(opt.converters) > 0 {
|
|
||||||
pair := converterPair{
|
|
||||||
SrcType: src.Type(),
|
|
||||||
DstType: dst.Type(),
|
|
||||||
}
|
|
||||||
|
|
||||||
if cnv, ok := opt.converters[pair]; ok {
|
|
||||||
return convert(dst, src, cnv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func convert(dst, src reflect.Value, fn convertFunc) (bool, error) {
|
|
||||||
result, err := fn(src.Interface())
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if result != nil {
|
|
||||||
dst.Set(reflect.ValueOf(result))
|
|
||||||
} else {
|
|
||||||
dst.Set(reflect.Zero(dst.Type()))
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFieldNameFromJsonTag(field reflect.StructField) string {
|
|
||||||
if tag := field.Tag.Get("json"); tag != "" {
|
|
||||||
if commaIndex := strings.Index(tag, ","); commaIndex != -1 {
|
|
||||||
name := tag[:commaIndex]
|
|
||||||
if name != "" {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
} else if tag != "" {
|
|
||||||
return tag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return field.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFieldName(name string, tag *tagOption, opt *options) string {
|
|
||||||
if tag != nil && tag.Contains(tagToName) {
|
|
||||||
return tag.toname
|
|
||||||
}
|
|
||||||
|
|
||||||
return opt.NameConvert(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func fieldByName(v reflect.Value, name string, opt *options) reflect.Value {
|
|
||||||
if opt != nil && opt.caseSensitive {
|
|
||||||
return v.FieldByName(name)
|
|
||||||
}
|
|
||||||
return v.FieldByNameFunc(func(s string) bool { return strings.EqualFold(s, name) })
|
|
||||||
}
|
|
||||||
|
|
||||||
func indirect(reflectValue reflect.Value) reflect.Value {
|
|
||||||
for reflectValue.Kind() == reflect.Pointer {
|
|
||||||
reflectValue = reflectValue.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
return reflectValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func indirectType(reflectType reflect.Type) (_ reflect.Type, isPtr bool) {
|
|
||||||
for reflectType.Kind() == reflect.Pointer || reflectType.Kind() == reflect.Slice {
|
|
||||||
reflectType = reflectType.Elem()
|
|
||||||
isPtr = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return reflectType, isPtr
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user