mirror of
https://gitee.com/johng/gf
synced 2026-06-06 16:21:40 +08:00
up
This commit is contained in:
@ -14,6 +14,8 @@ import (
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/structcache"
|
||||
)
|
||||
|
||||
type AnyConvertFunc = structcache.AnyConvertFunc
|
||||
|
||||
var (
|
||||
// Empty strings.
|
||||
emptyStringMap = map[string]struct{}{
|
||||
@ -29,17 +31,7 @@ var (
|
||||
// Note that only pointer can implement interface IUnmarshalValue.
|
||||
type IUnmarshalValue = localinterface.IUnmarshalValue
|
||||
|
||||
func init() {
|
||||
// register common converters for internal usage.
|
||||
structcache.RegisterCommonConverter(structcache.CommonConverter{
|
||||
Int64: Int64,
|
||||
Uint64: Uint64,
|
||||
String: String,
|
||||
Float32: Float32,
|
||||
Float64: Float64,
|
||||
Time: Time,
|
||||
GTime: GTime,
|
||||
Bytes: Bytes,
|
||||
Bool: Bool,
|
||||
})
|
||||
}
|
||||
var (
|
||||
// defaultConvertConfig is the default configuration for type converting.
|
||||
defaultConvertConfig = NewConvertConfig()
|
||||
)
|
||||
|
||||
@ -21,12 +21,15 @@ import (
|
||||
// The optional parameter `extraParams` is used for additional necessary parameter for this conversion.
|
||||
// It supports common basic types conversion as its conversion based on type name string.
|
||||
func Convert(fromValue interface{}, toTypeName string, extraParams ...interface{}) interface{} {
|
||||
return doConvert(doConvertInput{
|
||||
FromValue: fromValue,
|
||||
ToTypeName: toTypeName,
|
||||
ReferValue: nil,
|
||||
Extra: extraParams,
|
||||
})
|
||||
return doConvert(
|
||||
defaultConvertConfig,
|
||||
doConvertInput{
|
||||
FromValue: fromValue,
|
||||
ToTypeName: toTypeName,
|
||||
ReferValue: nil,
|
||||
Extra: extraParams,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// ConvertWithRefer converts the variable `fromValue` to the type referred by value `referValue`.
|
||||
@ -40,12 +43,15 @@ func ConvertWithRefer(fromValue interface{}, referValue interface{}, extraParams
|
||||
} else {
|
||||
referValueRf = reflect.ValueOf(referValue)
|
||||
}
|
||||
return doConvert(doConvertInput{
|
||||
FromValue: fromValue,
|
||||
ToTypeName: referValueRf.Type().String(),
|
||||
ReferValue: referValue,
|
||||
Extra: extraParams,
|
||||
})
|
||||
return doConvert(
|
||||
defaultConvertConfig,
|
||||
doConvertInput{
|
||||
FromValue: fromValue,
|
||||
ToTypeName: referValueRf.Type().String(),
|
||||
ReferValue: referValue,
|
||||
Extra: extraParams,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
type doConvertInput struct {
|
||||
@ -53,13 +59,14 @@ type doConvertInput struct {
|
||||
ToTypeName string // Target value type name in string.
|
||||
ReferValue interface{} // Referred value, a value in type `ToTypeName`. Note that its type might be reflect.Value.
|
||||
Extra []interface{} // Extra values for implementing the converting.
|
||||
|
||||
// Marks that the value is already converted and set to `ReferValue`. Caller can ignore the returned result.
|
||||
// It is an attribute for internal usage purpose.
|
||||
alreadySetToReferValue bool
|
||||
}
|
||||
|
||||
// doConvert does commonly use types converting.
|
||||
func doConvert(in doConvertInput) (convertedValue interface{}) {
|
||||
func doConvert(cf *ConvertConfig, in doConvertInput) (convertedValue interface{}) {
|
||||
switch in.ToTypeName {
|
||||
case "int":
|
||||
return Int(in.FromValue)
|
||||
@ -296,14 +303,14 @@ func doConvert(in doConvertInput) (convertedValue interface{}) {
|
||||
}
|
||||
|
||||
// custom converter.
|
||||
if dstReflectValue, ok, _ := callCustomConverterWithRefer(fromReflectValue, referReflectValue); ok {
|
||||
if dstReflectValue, ok, _ := cf.callCustomConverterWithRefer(fromReflectValue, referReflectValue); ok {
|
||||
return dstReflectValue.Interface()
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if recover() != nil {
|
||||
in.alreadySetToReferValue = false
|
||||
if err := bindVarToReflectValue(referReflectValue, in.FromValue, nil); err == nil {
|
||||
if err := bindVarToReflectValue(cf, referReflectValue, in.FromValue, nil); err == nil {
|
||||
in.alreadySetToReferValue = true
|
||||
convertedValue = referReflectValue.Interface()
|
||||
}
|
||||
@ -326,7 +333,7 @@ func doConvert(in doConvertInput) (convertedValue interface{}) {
|
||||
default:
|
||||
in.ToTypeName = originType.Kind().String()
|
||||
in.ReferValue = nil
|
||||
refElementValue := reflect.ValueOf(doConvert(in))
|
||||
refElementValue := reflect.ValueOf(doConvert(cf, in))
|
||||
originTypeValue := reflect.New(refElementValue.Type()).Elem()
|
||||
originTypeValue.Set(refElementValue)
|
||||
in.alreadySetToReferValue = true
|
||||
@ -335,7 +342,7 @@ func doConvert(in doConvertInput) (convertedValue interface{}) {
|
||||
|
||||
case reflect.Map:
|
||||
var targetValue = reflect.New(referReflectValue.Type()).Elem()
|
||||
if err := doMapToMap(in.FromValue, targetValue); err == nil {
|
||||
if err := doMapToMap(cf, in.FromValue, targetValue); err == nil {
|
||||
in.alreadySetToReferValue = true
|
||||
}
|
||||
return targetValue.Interface()
|
||||
@ -343,15 +350,15 @@ func doConvert(in doConvertInput) (convertedValue interface{}) {
|
||||
in.ToTypeName = referReflectValue.Kind().String()
|
||||
in.ReferValue = nil
|
||||
in.alreadySetToReferValue = true
|
||||
convertedValue = reflect.ValueOf(doConvert(in)).Convert(referReflectValue.Type()).Interface()
|
||||
convertedValue = reflect.ValueOf(doConvert(cf, in)).Convert(referReflectValue.Type()).Interface()
|
||||
return convertedValue
|
||||
}
|
||||
return in.FromValue
|
||||
}
|
||||
}
|
||||
|
||||
func doConvertWithReflectValueSet(reflectValue reflect.Value, in doConvertInput) {
|
||||
convertedValue := doConvert(in)
|
||||
func doConvertWithReflectValueSet(cf *ConvertConfig, reflectValue reflect.Value, in doConvertInput) {
|
||||
convertedValue := doConvert(cf, in)
|
||||
if !in.alreadySetToReferValue {
|
||||
reflectValue.Set(reflect.ValueOf(convertedValue))
|
||||
}
|
||||
|
||||
99
util/gconv/gconv_convert_config.go
Normal file
99
util/gconv/gconv_convert_config.go
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/structcache"
|
||||
)
|
||||
|
||||
// CommonTypeConverter holds some converting functions of common types for internal usage.
|
||||
type CommonTypeConverter = structcache.CommonTypeConverter
|
||||
|
||||
type (
|
||||
converterInType = reflect.Type
|
||||
converterOutType = reflect.Type
|
||||
converterFunc = reflect.Value
|
||||
)
|
||||
|
||||
// ConvertConfig is the configuration for type converting.
|
||||
type ConvertConfig struct {
|
||||
internalConvertConfig *structcache.ConvertConfig
|
||||
// customConverters for internal converter storing.
|
||||
customConverters map[converterInType]map[converterOutType]converterFunc
|
||||
}
|
||||
|
||||
var (
|
||||
intType = reflect.TypeOf(0)
|
||||
int8Type = reflect.TypeOf(int8(0))
|
||||
int16Type = reflect.TypeOf(int16(0))
|
||||
int32Type = reflect.TypeOf(int32(0))
|
||||
int64Type = reflect.TypeOf(int64(0))
|
||||
|
||||
uintType = reflect.TypeOf(uint(0))
|
||||
uint8Type = reflect.TypeOf(uint8(0))
|
||||
uint16Type = reflect.TypeOf(uint16(0))
|
||||
uint32Type = reflect.TypeOf(uint32(0))
|
||||
uint64Type = reflect.TypeOf(uint64(0))
|
||||
|
||||
float32Type = reflect.TypeOf(float32(0))
|
||||
float64Type = reflect.TypeOf(float64(0))
|
||||
|
||||
stringType = reflect.TypeOf("")
|
||||
bytesType = reflect.TypeOf([]byte{})
|
||||
|
||||
boolType = reflect.TypeOf(false)
|
||||
|
||||
timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
|
||||
gtimeType = reflect.TypeOf((*gtime.Time)(nil)).Elem()
|
||||
)
|
||||
|
||||
// NewConvertConfig creates and returns configuration management object for type converting.
|
||||
func NewConvertConfig() *ConvertConfig {
|
||||
cf := &ConvertConfig{
|
||||
internalConvertConfig: structcache.NewConvertConfig(),
|
||||
customConverters: make(map[converterInType]map[converterOutType]converterFunc),
|
||||
}
|
||||
cf.registerBuiltInConverter()
|
||||
return cf
|
||||
}
|
||||
|
||||
func (cf *ConvertConfig) registerBuiltInConverter() {
|
||||
cf.registerAnyConvertFuncForTypes(
|
||||
builtInAnyConvertFuncForInt64, intType, int8Type, int16Type, int32Type, int64Type,
|
||||
)
|
||||
cf.registerAnyConvertFuncForTypes(
|
||||
builtInAnyConvertFuncForUint64, uintType, uint8Type, uint16Type, uint32Type, uint64Type,
|
||||
)
|
||||
cf.registerAnyConvertFuncForTypes(
|
||||
builtInAnyConvertFuncForString, stringType,
|
||||
)
|
||||
cf.registerAnyConvertFuncForTypes(
|
||||
builtInAnyConvertFuncForFloat64, float32Type, float64Type,
|
||||
)
|
||||
cf.registerAnyConvertFuncForTypes(
|
||||
builtInAnyConvertFuncForBool, boolType,
|
||||
)
|
||||
cf.registerAnyConvertFuncForTypes(
|
||||
builtInAnyConvertFuncForBytes, bytesType,
|
||||
)
|
||||
cf.registerAnyConvertFuncForTypes(
|
||||
builtInAnyConvertFuncForTime, timeType,
|
||||
)
|
||||
cf.registerAnyConvertFuncForTypes(
|
||||
builtInAnyConvertFuncForGTime, gtimeType,
|
||||
)
|
||||
}
|
||||
|
||||
func (cf *ConvertConfig) registerAnyConvertFuncForTypes(convertFunc AnyConvertFunc, types ...reflect.Type) {
|
||||
for _, t := range types {
|
||||
cf.internalConvertConfig.RegisterAnyConvertFunc(t, convertFunc)
|
||||
}
|
||||
}
|
||||
@ -11,19 +11,14 @@ import (
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/structcache"
|
||||
)
|
||||
|
||||
type (
|
||||
converterInType = reflect.Type
|
||||
converterOutType = reflect.Type
|
||||
converterFunc = reflect.Value
|
||||
)
|
||||
// RegisterConverter registers custom converter.
|
||||
func RegisterConverter(fn any) (err error) {
|
||||
return defaultConvertConfig.RegisterConverter(fn)
|
||||
}
|
||||
|
||||
// customConverters for internal converter storing.
|
||||
var customConverters = make(map[converterInType]map[converterOutType]converterFunc)
|
||||
|
||||
// RegisterConverter to register custom converter.
|
||||
// RegisterConverter registers custom converter.
|
||||
// It must be registered before you use this custom converting feature.
|
||||
// It is suggested to do it in boot procedure of the process.
|
||||
//
|
||||
@ -31,7 +26,7 @@ var customConverters = make(map[converterInType]map[converterOutType]converterFu
|
||||
// 1. The parameter `fn` must be defined as pattern `func(T1) (T2, error)`.
|
||||
// It will convert type `T1` to type `T2`.
|
||||
// 2. The `T1` should not be type of pointer, but the `T2` should be type of pointer.
|
||||
func RegisterConverter(fn interface{}) (err error) {
|
||||
func (cf *ConvertConfig) RegisterConverter(fn any) (err error) {
|
||||
var (
|
||||
fnReflectType = reflect.TypeOf(fn)
|
||||
errType = reflect.TypeOf((*error)(nil)).Elem()
|
||||
@ -41,7 +36,8 @@ func RegisterConverter(fn interface{}) (err error) {
|
||||
!fnReflectType.Out(1).Implements(errType) {
|
||||
err = gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
"parameter must be type of converter function and defined as pattern `func(T1) (T2, error)`, but defined as `%s`",
|
||||
"parameter must be type of converter function and defined as pattern `func(T1) (T2, error)`, "+
|
||||
"but defined as `%s`",
|
||||
fnReflectType.String(),
|
||||
)
|
||||
return
|
||||
@ -69,10 +65,10 @@ func RegisterConverter(fn interface{}) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
registeredOutTypeMap, ok := customConverters[inType]
|
||||
registeredOutTypeMap, ok := cf.customConverters[inType]
|
||||
if !ok {
|
||||
registeredOutTypeMap = make(map[converterOutType]converterFunc)
|
||||
customConverters[inType] = registeredOutTypeMap
|
||||
cf.customConverters[inType] = registeredOutTypeMap
|
||||
}
|
||||
if _, ok = registeredOutTypeMap[outType]; ok {
|
||||
err = gerror.NewCodef(
|
||||
@ -83,14 +79,14 @@ func RegisterConverter(fn interface{}) (err error) {
|
||||
return
|
||||
}
|
||||
registeredOutTypeMap[outType] = reflect.ValueOf(fn)
|
||||
structcache.RegisterCustomConvertType(outType)
|
||||
cf.internalConvertConfig.RegisterCustomConvertType(outType)
|
||||
return
|
||||
}
|
||||
|
||||
func getRegisteredConverterFuncAndSrcType(
|
||||
func (cf *ConvertConfig) getRegisteredConverterFuncAndSrcType(
|
||||
srcReflectValue, dstReflectValueForRefer reflect.Value,
|
||||
) (f converterFunc, srcType reflect.Type, ok bool) {
|
||||
if len(customConverters) == 0 {
|
||||
if len(cf.customConverters) == 0 {
|
||||
return reflect.Value{}, nil, false
|
||||
}
|
||||
srcType = srcReflectValue.Type()
|
||||
@ -99,7 +95,7 @@ func getRegisteredConverterFuncAndSrcType(
|
||||
}
|
||||
var registeredOutTypeMap map[converterOutType]converterFunc
|
||||
// firstly, it searches the map by input parameter type.
|
||||
registeredOutTypeMap, ok = customConverters[srcType]
|
||||
registeredOutTypeMap, ok = cf.customConverters[srcType]
|
||||
if !ok {
|
||||
return reflect.Value{}, nil, false
|
||||
}
|
||||
@ -123,28 +119,28 @@ func getRegisteredConverterFuncAndSrcType(
|
||||
return
|
||||
}
|
||||
|
||||
func callCustomConverterWithRefer(
|
||||
func (cf *ConvertConfig) callCustomConverterWithRefer(
|
||||
srcReflectValue, referReflectValue reflect.Value,
|
||||
) (dstReflectValue reflect.Value, converted bool, err error) {
|
||||
registeredConverterFunc, srcType, ok := getRegisteredConverterFuncAndSrcType(srcReflectValue, referReflectValue)
|
||||
registeredConverterFunc, srcType, ok := cf.getRegisteredConverterFuncAndSrcType(srcReflectValue, referReflectValue)
|
||||
if !ok {
|
||||
return reflect.Value{}, false, nil
|
||||
}
|
||||
dstReflectValue = reflect.New(referReflectValue.Type()).Elem()
|
||||
converted, err = doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType)
|
||||
converted, err = cf.doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType)
|
||||
return
|
||||
}
|
||||
|
||||
// callCustomConverter call the custom converter. It will try some possible type.
|
||||
func callCustomConverter(srcReflectValue, dstReflectValue reflect.Value) (converted bool, err error) {
|
||||
registeredConverterFunc, srcType, ok := getRegisteredConverterFuncAndSrcType(srcReflectValue, dstReflectValue)
|
||||
func (cf *ConvertConfig) callCustomConverter(srcReflectValue, dstReflectValue reflect.Value) (converted bool, err error) {
|
||||
registeredConverterFunc, srcType, ok := cf.getRegisteredConverterFuncAndSrcType(srcReflectValue, dstReflectValue)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
return doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType)
|
||||
return cf.doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType)
|
||||
}
|
||||
|
||||
func doCallCustomConverter(
|
||||
func (cf *ConvertConfig) doCallCustomConverter(
|
||||
srcReflectValue reflect.Value,
|
||||
dstReflectValue reflect.Value,
|
||||
registeredConverterFunc converterFunc,
|
||||
|
||||
82
util/gconv/gconv_converter_builtin.go
Normal file
82
util/gconv/gconv_converter_builtin.go
Normal file
@ -0,0 +1,82 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
func builtInAnyConvertFuncForInt64(from any, to reflect.Value) error {
|
||||
v, err := doInt64(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
to.SetInt(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func builtInAnyConvertFuncForUint64(from any, to reflect.Value) error {
|
||||
v, err := doUint64(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
to.SetUint(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func builtInAnyConvertFuncForString(from any, to reflect.Value) error {
|
||||
v, err := doString(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
to.SetString(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func builtInAnyConvertFuncForFloat64(from any, to reflect.Value) error {
|
||||
v, err := doFloat64(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
to.SetFloat(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func builtInAnyConvertFuncForBool(from any, to reflect.Value) error {
|
||||
v, err := doBool(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
to.SetBool(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func builtInAnyConvertFuncForBytes(from any, to reflect.Value) error {
|
||||
v, err := doBytes(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
to.SetBytes(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func builtInAnyConvertFuncForTime(from any, to reflect.Value) error {
|
||||
*to.Addr().Interface().(*time.Time) = Time(from)
|
||||
return nil
|
||||
}
|
||||
|
||||
func builtInAnyConvertFuncForGTime(from any, to reflect.Value) error {
|
||||
v := GTime(from)
|
||||
if v == nil {
|
||||
v = gtime.New()
|
||||
}
|
||||
*to.Addr().Interface().(*gtime.Time) = *v
|
||||
return nil
|
||||
}
|
||||
@ -16,7 +16,7 @@ import (
|
||||
// MapToMap converts any map type variable `params` to another map type variable `pointer`
|
||||
// using reflect.
|
||||
// See doMapToMap.
|
||||
func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) error {
|
||||
func MapToMap(params any, pointer any, mapping ...map[string]string) error {
|
||||
return Scan(params, pointer, mapping...)
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]str
|
||||
//
|
||||
// The optional parameter `mapping` is used for struct attribute to map key mapping, which makes
|
||||
// sense only if the items of original map `params` is type struct.
|
||||
func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
func doMapToMap(cf *ConvertConfig, params any, pointer any, mapping ...map[string]string) (err error) {
|
||||
var (
|
||||
paramsRv reflect.Value
|
||||
paramsKind reflect.Kind
|
||||
@ -50,7 +50,7 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s
|
||||
paramsKind = paramsRv.Kind()
|
||||
}
|
||||
if paramsKind != reflect.Map {
|
||||
return doMapToMap(Map(params), pointer, mapping...)
|
||||
return doMapToMap(cf, Map(params), pointer, mapping...)
|
||||
}
|
||||
// Empty params map, no need continue.
|
||||
if paramsRv.Len() == 0 {
|
||||
@ -107,22 +107,26 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s
|
||||
default:
|
||||
mapValue.Set(
|
||||
reflect.ValueOf(
|
||||
doConvert(doConvertInput{
|
||||
FromValue: paramsRv.MapIndex(key).Interface(),
|
||||
ToTypeName: pointerValueType.String(),
|
||||
ReferValue: mapValue,
|
||||
Extra: nil,
|
||||
}),
|
||||
doConvert(
|
||||
cf, doConvertInput{
|
||||
FromValue: paramsRv.MapIndex(key).Interface(),
|
||||
ToTypeName: pointerValueType.String(),
|
||||
ReferValue: mapValue,
|
||||
Extra: nil,
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
var mapKey = reflect.ValueOf(
|
||||
doConvert(doConvertInput{
|
||||
FromValue: key.Interface(),
|
||||
ToTypeName: pointerKeyType.Name(),
|
||||
ReferValue: reflect.New(pointerKeyType).Elem().Interface(),
|
||||
Extra: nil,
|
||||
}),
|
||||
doConvert(
|
||||
cf,
|
||||
doConvertInput{
|
||||
FromValue: key.Interface(),
|
||||
ToTypeName: pointerKeyType.Name(),
|
||||
ReferValue: reflect.New(pointerKeyType).Elem().Interface(),
|
||||
Extra: nil,
|
||||
},
|
||||
),
|
||||
)
|
||||
dataMap.SetMapIndex(mapKey, mapValue)
|
||||
}
|
||||
|
||||
@ -26,6 +26,10 @@ import (
|
||||
// The `paramKeyToAttrMap` parameter is used for mapping between attribute names and parameter keys.
|
||||
// TODO: change `paramKeyToAttrMap` to `ScanOption` to be more scalable; add `DeepCopy` option for `ScanOption`.
|
||||
func Scan(srcValue any, dstPointer any, paramKeyToAttrMap ...map[string]string) (err error) {
|
||||
return ScanWithConfig(defaultConvertConfig, srcValue, dstPointer, paramKeyToAttrMap...)
|
||||
}
|
||||
|
||||
func ScanWithConfig(cf *ConvertConfig, srcValue any, dstPointer any, paramKeyToAttrMap ...map[string]string) (err error) {
|
||||
// Check if srcValue is nil, in which case no conversion is needed
|
||||
if srcValue == nil {
|
||||
return nil
|
||||
@ -142,7 +146,7 @@ func Scan(srcValue any, dstPointer any, paramKeyToAttrMap ...map[string]string)
|
||||
}
|
||||
// Special handling for struct or map slice elements
|
||||
if dstElemKind == reflect.Struct || dstElemKind == reflect.Map {
|
||||
return doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...)
|
||||
return doScanForComplicatedTypes(cf, srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...)
|
||||
}
|
||||
// Handle basic type slice conversions
|
||||
var srcValueReflectValueKind = srcValueReflectValue.Kind()
|
||||
@ -173,11 +177,11 @@ func Scan(srcValue any, dstPointer any, paramKeyToAttrMap ...map[string]string)
|
||||
dstPointerReflectValueElem.Set(newSlice)
|
||||
return nil
|
||||
}
|
||||
return doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...)
|
||||
return doScanForComplicatedTypes(cf, srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...)
|
||||
|
||||
default:
|
||||
// Handle complex types (structs, maps, etc.)
|
||||
return doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...)
|
||||
return doScanForComplicatedTypes(cf, srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...)
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,6 +197,7 @@ func Scan(srcValue any, dstPointer any, paramKeyToAttrMap ...map[string]string)
|
||||
// - dstPointerReflectType: The reflection type of the destination pointer
|
||||
// - paramKeyToAttrMap: Optional mapping between parameter keys and struct attribute names
|
||||
func doScanForComplicatedTypes(
|
||||
cf *ConvertConfig,
|
||||
srcValue, dstPointer any,
|
||||
dstPointerReflectType reflect.Type,
|
||||
paramKeyToAttrMap ...map[string]string,
|
||||
@ -220,7 +225,7 @@ func doScanForComplicatedTypes(
|
||||
switch dstPointerReflectTypeElemKind {
|
||||
case reflect.Map:
|
||||
// Convert map to map
|
||||
return doMapToMap(srcValue, dstPointer, paramKeyToAttrMap...)
|
||||
return doMapToMap(cf, srcValue, dstPointer, paramKeyToAttrMap...)
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
var (
|
||||
|
||||
@ -32,20 +32,34 @@ import (
|
||||
// It will automatically convert the first letter of the key to uppercase
|
||||
// in mapping procedure to do the matching.
|
||||
// It ignores the map key, if it does not match.
|
||||
func Struct(params interface{}, pointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) {
|
||||
func Struct(params any, pointer any, paramKeyToAttrMap ...map[string]string) (err error) {
|
||||
return Scan(params, pointer, paramKeyToAttrMap...)
|
||||
}
|
||||
|
||||
// StructTag acts as Struct but also with support for priority tag feature, which retrieves the
|
||||
// specified priorityTagAndFieldName for `params` key-value items to struct attribute names mapping.
|
||||
// The parameter `priorityTag` supports multiple priorityTagAndFieldName that can be joined with char ','.
|
||||
func StructTag(params interface{}, pointer interface{}, priorityTag string) (err error) {
|
||||
func StructTag(params any, pointer any, priorityTag string) (err error) {
|
||||
return doStruct(params, pointer, nil, priorityTag)
|
||||
}
|
||||
|
||||
// doStruct is the core internal converting function for any data to struct.
|
||||
func doStruct(
|
||||
params interface{}, pointer interface{}, paramKeyToAttrMap map[string]string, priorityTag string,
|
||||
params any,
|
||||
pointer any,
|
||||
paramKeyToAttrMap map[string]string,
|
||||
priorityTag string,
|
||||
) (err error) {
|
||||
return doStructWithConfig(defaultConvertConfig, params, pointer, paramKeyToAttrMap, priorityTag)
|
||||
}
|
||||
|
||||
// doStruct is the core internal converting function for any data to struct.
|
||||
func doStructWithConfig(
|
||||
cf *ConvertConfig,
|
||||
params any,
|
||||
pointer any,
|
||||
paramKeyToAttrMap map[string]string,
|
||||
priorityTag string,
|
||||
) (err error) {
|
||||
if params == nil {
|
||||
// If `params` is nil, no conversion.
|
||||
@ -77,7 +91,7 @@ func doStruct(
|
||||
|
||||
var (
|
||||
paramsReflectValue reflect.Value
|
||||
paramsInterface interface{} // DO NOT use `params` directly as it might be type `reflect.Value`
|
||||
paramsInterface any // DO NOT use `params` directly as it might be type `reflect.Value`
|
||||
pointerReflectValue reflect.Value
|
||||
pointerReflectKind reflect.Kind
|
||||
pointerElemReflectValue reflect.Value // The reflection value to struct element.
|
||||
@ -118,7 +132,7 @@ func doStruct(
|
||||
}
|
||||
|
||||
// custom convert.
|
||||
if ok, err = callCustomConverter(paramsReflectValue, pointerReflectValue); ok {
|
||||
if ok, err = cf.callCustomConverter(paramsReflectValue, pointerReflectValue); ok {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -150,15 +164,15 @@ func doStruct(
|
||||
// Retrieve its element, may be struct at last.
|
||||
pointerElemReflectValue = pointerElemReflectValue.Elem()
|
||||
}
|
||||
paramsMap, ok := paramsInterface.(map[string]interface{})
|
||||
paramsMap, ok := paramsInterface.(map[string]any)
|
||||
if !ok {
|
||||
// paramsMap is the map[string]interface{} type variable for params.
|
||||
// paramsMap is the map[string]any type variable for params.
|
||||
// DO NOT use MapDeep here.
|
||||
paramsMap = doMapConvert(paramsInterface, recursiveTypeAuto, true)
|
||||
if paramsMap == nil {
|
||||
return gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`convert params from "%#v" to "map[string]interface{}" failed`,
|
||||
`convert params from "%#v" to "map[string]any" failed`,
|
||||
params,
|
||||
)
|
||||
}
|
||||
@ -168,7 +182,7 @@ func doStruct(
|
||||
return nil
|
||||
}
|
||||
// Get struct info from cache or parse struct and cache the struct info.
|
||||
cachedStructInfo := structcache.GetCachedStructInfo(
|
||||
cachedStructInfo := cf.internalConvertConfig.GetCachedStructInfo(
|
||||
pointerElemReflectValue.Type(), priorityTag,
|
||||
)
|
||||
// Nothing to be converted.
|
||||
@ -184,7 +198,7 @@ func doStruct(
|
||||
// Indicates that those values have been used and cannot be reused.
|
||||
usedParamsKeyOrTagNameMap = structcache.GetUsedParamsKeyOrTagNameMapFromPool()
|
||||
cachedFieldInfo *structcache.CachedFieldInfo
|
||||
paramsValue interface{}
|
||||
paramsValue any
|
||||
)
|
||||
defer structcache.PutUsedParamsKeyOrTagNameMapToPool(usedParamsKeyOrTagNameMap)
|
||||
|
||||
@ -199,16 +213,17 @@ func doStruct(
|
||||
if cachedFieldInfo != nil {
|
||||
fieldValue := cachedFieldInfo.GetFieldReflectValueFrom(pointerElemReflectValue)
|
||||
if err = bindVarToStructField(
|
||||
cf,
|
||||
cachedFieldInfo,
|
||||
fieldValue,
|
||||
paramsValue,
|
||||
cachedFieldInfo,
|
||||
paramKeyToAttrMap,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(cachedFieldInfo.OtherSameNameField) > 0 {
|
||||
if err = setOtherSameNameField(
|
||||
cachedFieldInfo, paramsValue, pointerReflectValue, paramKeyToAttrMap,
|
||||
cf, cachedFieldInfo, paramsValue, pointerReflectValue, paramKeyToAttrMap,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -221,11 +236,12 @@ func doStruct(
|
||||
return nil
|
||||
}
|
||||
return bindStructWithLoopFieldInfos(
|
||||
paramsMap, pointerElemReflectValue, paramKeyToAttrMap, usedParamsKeyOrTagNameMap, cachedStructInfo,
|
||||
cf, paramsMap, pointerElemReflectValue, paramKeyToAttrMap, usedParamsKeyOrTagNameMap, cachedStructInfo,
|
||||
)
|
||||
}
|
||||
|
||||
func setOtherSameNameField(
|
||||
cf *ConvertConfig,
|
||||
cachedFieldInfo *structcache.CachedFieldInfo,
|
||||
srcValue any,
|
||||
structValue reflect.Value,
|
||||
@ -234,7 +250,7 @@ func setOtherSameNameField(
|
||||
// loop the same field name of all sub attributes.
|
||||
for _, otherFieldInfo := range cachedFieldInfo.OtherSameNameField {
|
||||
fieldValue := cachedFieldInfo.GetOtherFieldReflectValueFrom(structValue, otherFieldInfo.FieldIndexes)
|
||||
if err = bindVarToStructField(fieldValue, srcValue, otherFieldInfo, paramKeyToAttrMap); err != nil {
|
||||
if err = bindVarToStructField(cf, otherFieldInfo, fieldValue, srcValue, paramKeyToAttrMap); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -242,6 +258,7 @@ func setOtherSameNameField(
|
||||
}
|
||||
|
||||
func bindStructWithLoopFieldInfos(
|
||||
cf *ConvertConfig,
|
||||
paramsMap map[string]any,
|
||||
structValue reflect.Value,
|
||||
paramKeyToAttrMap map[string]string,
|
||||
@ -257,21 +274,21 @@ func bindStructWithLoopFieldInfos(
|
||||
matched bool
|
||||
ok bool
|
||||
)
|
||||
for _, cachedFieldInfo = range cachedStructInfo.FieldConvertInfos {
|
||||
for _, cachedFieldInfo = range cachedStructInfo.GetFieldConvertInfos() {
|
||||
for _, fieldTag := range cachedFieldInfo.PriorityTagAndFieldName {
|
||||
if paramValue, ok = paramsMap[fieldTag]; !ok {
|
||||
continue
|
||||
}
|
||||
fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue)
|
||||
if err = bindVarToStructField(
|
||||
fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap,
|
||||
cf, cachedFieldInfo, fieldValue, paramValue, paramKeyToAttrMap,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
// handle same field name in nested struct.
|
||||
if len(cachedFieldInfo.OtherSameNameField) > 0 {
|
||||
if err = setOtherSameNameField(
|
||||
cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap,
|
||||
cf, cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -297,14 +314,14 @@ func bindStructWithLoopFieldInfos(
|
||||
fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue)
|
||||
if paramValue != nil {
|
||||
if err = bindVarToStructField(
|
||||
fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap,
|
||||
cf, cachedFieldInfo, fieldValue, paramValue, paramKeyToAttrMap,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
// handle same field name in nested struct.
|
||||
if len(cachedFieldInfo.OtherSameNameField) > 0 {
|
||||
if err = setOtherSameNameField(
|
||||
cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap,
|
||||
cf, cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -338,9 +355,10 @@ func fuzzyMatchingFieldName(
|
||||
// bindVarToStructField sets value to struct object attribute by name.
|
||||
// each value to attribute converting comes into in this function.
|
||||
func bindVarToStructField(
|
||||
fieldValue reflect.Value,
|
||||
srcValue interface{},
|
||||
cf *ConvertConfig,
|
||||
cachedFieldInfo *structcache.CachedFieldInfo,
|
||||
fieldValue reflect.Value,
|
||||
srcValue any,
|
||||
paramKeyToAttrMap map[string]string,
|
||||
) (err error) {
|
||||
if !fieldValue.IsValid() {
|
||||
@ -352,7 +370,7 @@ func bindVarToStructField(
|
||||
}
|
||||
defer func() {
|
||||
if exception := recover(); exception != nil {
|
||||
if err = bindVarToReflectValue(fieldValue, srcValue, paramKeyToAttrMap); err != nil {
|
||||
if err = bindVarToReflectValue(cf, fieldValue, srcValue, paramKeyToAttrMap); err != nil {
|
||||
err = gerror.Wrapf(err, `error binding srcValue to attribute "%s"`, cachedFieldInfo.FieldName())
|
||||
}
|
||||
}
|
||||
@ -372,7 +390,7 @@ func bindVarToStructField(
|
||||
if customConverterInput, ok = srcValue.(reflect.Value); !ok {
|
||||
customConverterInput = reflect.ValueOf(srcValue)
|
||||
}
|
||||
if ok, err = callCustomConverter(customConverterInput, fieldValue); ok || err != nil {
|
||||
if ok, err = cf.callCustomConverter(customConverterInput, fieldValue); ok || err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -383,20 +401,21 @@ func bindVarToStructField(
|
||||
}
|
||||
// Common types use fast assignment logic
|
||||
if cachedFieldInfo.ConvertFunc != nil {
|
||||
cachedFieldInfo.ConvertFunc(srcValue, fieldValue)
|
||||
return nil
|
||||
return cachedFieldInfo.ConvertFunc(srcValue, fieldValue)
|
||||
}
|
||||
doConvertWithReflectValueSet(fieldValue, doConvertInput{
|
||||
FromValue: srcValue,
|
||||
ToTypeName: cachedFieldInfo.StructField.Type.String(),
|
||||
ReferValue: fieldValue,
|
||||
})
|
||||
doConvertWithReflectValueSet(
|
||||
cf, fieldValue, doConvertInput{
|
||||
FromValue: srcValue,
|
||||
ToTypeName: cachedFieldInfo.StructField.Type.String(),
|
||||
ReferValue: fieldValue,
|
||||
},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindVarToReflectValueWithInterfaceCheck does bind using common interfaces checks.
|
||||
func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value interface{}) (bool, error) {
|
||||
var pointer interface{}
|
||||
func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value any) (bool, error) {
|
||||
var pointer any
|
||||
if reflectValue.Kind() != reflect.Ptr && reflectValue.CanAddr() {
|
||||
reflectValueAddr := reflectValue.Addr()
|
||||
if reflectValueAddr.IsNil() || !reflectValueAddr.IsValid() {
|
||||
@ -460,7 +479,7 @@ func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value i
|
||||
|
||||
// bindVarToReflectValue sets `value` to reflect value object `structFieldValue`.
|
||||
func bindVarToReflectValue(
|
||||
structFieldValue reflect.Value, value interface{}, paramKeyToAttrMap map[string]string,
|
||||
cf *ConvertConfig, structFieldValue reflect.Value, value any, paramKeyToAttrMap map[string]string,
|
||||
) (err error) {
|
||||
// JSON content converting.
|
||||
ok, err := doConvertWithJsonCheck(value, structFieldValue)
|
||||
@ -486,7 +505,7 @@ func bindVarToReflectValue(
|
||||
// Converting using reflection by kind.
|
||||
switch kind {
|
||||
case reflect.Map:
|
||||
return doMapToMap(value, structFieldValue, paramKeyToAttrMap)
|
||||
return doMapToMap(cf, value, structFieldValue, paramKeyToAttrMap)
|
||||
|
||||
case reflect.Struct:
|
||||
// Recursively converting for struct attribute.
|
||||
@ -528,11 +547,13 @@ func bindVarToReflectValue(
|
||||
}
|
||||
}
|
||||
if !converted {
|
||||
doConvertWithReflectValueSet(elem, doConvertInput{
|
||||
FromValue: reflectValue.Index(i).Interface(),
|
||||
ToTypeName: elemTypeName,
|
||||
ReferValue: elem,
|
||||
})
|
||||
doConvertWithReflectValueSet(
|
||||
cf, elem, doConvertInput{
|
||||
FromValue: reflectValue.Index(i).Interface(),
|
||||
ToTypeName: elemTypeName,
|
||||
ReferValue: elem,
|
||||
},
|
||||
)
|
||||
}
|
||||
if elemType.Kind() == reflect.Ptr {
|
||||
// Before it sets the `elem` to array, do pointer converting if necessary.
|
||||
@ -578,11 +599,13 @@ func bindVarToReflectValue(
|
||||
}
|
||||
}
|
||||
if !converted {
|
||||
doConvertWithReflectValueSet(elem, doConvertInput{
|
||||
FromValue: value,
|
||||
ToTypeName: elemTypeName,
|
||||
ReferValue: elem,
|
||||
})
|
||||
doConvertWithReflectValueSet(
|
||||
cf, elem, doConvertInput{
|
||||
FromValue: value,
|
||||
ToTypeName: elemTypeName,
|
||||
ReferValue: elem,
|
||||
},
|
||||
)
|
||||
}
|
||||
if elemType.Kind() == reflect.Ptr {
|
||||
// Before it sets the `elem` to array, do pointer converting if necessary.
|
||||
@ -602,19 +625,19 @@ func bindVarToReflectValue(
|
||||
return err
|
||||
}
|
||||
elem := item.Elem()
|
||||
if err = bindVarToReflectValue(elem, value, paramKeyToAttrMap); err == nil {
|
||||
if err = bindVarToReflectValue(cf, elem, value, paramKeyToAttrMap); err == nil {
|
||||
structFieldValue.Set(elem.Addr())
|
||||
}
|
||||
} else {
|
||||
// Not empty pointer, it assigns values to it.
|
||||
return bindVarToReflectValue(structFieldValue.Elem(), value, paramKeyToAttrMap)
|
||||
return bindVarToReflectValue(cf, structFieldValue.Elem(), value, paramKeyToAttrMap)
|
||||
}
|
||||
|
||||
// It mainly and specially handles the interface of nil value.
|
||||
case reflect.Interface:
|
||||
if value == nil {
|
||||
// Specially.
|
||||
structFieldValue.Set(reflect.ValueOf((*interface{})(nil)))
|
||||
structFieldValue.Set(reflect.ValueOf((*any)(nil)))
|
||||
} else {
|
||||
// Note there's reflect conversion mechanism here.
|
||||
structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
|
||||
|
||||
@ -9,24 +9,64 @@ package structcache
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
)
|
||||
|
||||
var (
|
||||
type AnyConvertFunc func(from any, to reflect.Value) error
|
||||
|
||||
// ConvertConfig is the configuration for type converting.
|
||||
type ConvertConfig struct {
|
||||
// map[reflect.Type]*CachedStructInfo
|
||||
cachedStructsInfoMap sync.Map
|
||||
|
||||
// customConvertTypeMap is used to store whether field types are registered to custom conversions
|
||||
// For example:
|
||||
// func (src *TypeA) (dst *TypeB,err error)
|
||||
// This map will store `TypeB` for quick judgment during assignment.
|
||||
customConvertTypeMap = map[reflect.Type]struct{}{}
|
||||
)
|
||||
// TODO remove?
|
||||
customConvertTypeMap map[reflect.Type]struct{}
|
||||
|
||||
// anyToTypeConvertMap for custom type converting from any to its reflect.Value.
|
||||
anyToTypeConvertMap map[reflect.Type]AnyConvertFunc
|
||||
}
|
||||
|
||||
// CommonTypeConverter holds some converting functions of common types for internal usage.
|
||||
type CommonTypeConverter struct {
|
||||
Int64 func(v any) (int64, error)
|
||||
Uint64 func(v any) (uint64, error)
|
||||
String func(v any) (string, error)
|
||||
Float32 func(v any) (float32, error)
|
||||
Float64 func(v any) (float64, error)
|
||||
Time func(v any, format ...string) (time.Time, error)
|
||||
GTime func(v any, format ...string) (*gtime.Time, error)
|
||||
Bytes func(v any) ([]byte, error)
|
||||
Bool func(v any) (bool, error)
|
||||
}
|
||||
|
||||
// NewConvertConfig creates and returns a new ConvertConfig object.
|
||||
func NewConvertConfig() *ConvertConfig {
|
||||
return &ConvertConfig{
|
||||
cachedStructsInfoMap: sync.Map{},
|
||||
customConvertTypeMap: make(map[reflect.Type]struct{}),
|
||||
anyToTypeConvertMap: make(map[reflect.Type]AnyConvertFunc),
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterCustomConvertType registers custom
|
||||
func RegisterCustomConvertType(fieldType reflect.Type) {
|
||||
func (cf *ConvertConfig) RegisterCustomConvertType(fieldType reflect.Type) {
|
||||
if fieldType.Kind() == reflect.Ptr {
|
||||
fieldType = fieldType.Elem()
|
||||
}
|
||||
customConvertTypeMap[fieldType] = struct{}{}
|
||||
cf.customConvertTypeMap[fieldType] = struct{}{}
|
||||
}
|
||||
|
||||
// RegisterAnyConvertFunc registers custom type converting function for specified type.
|
||||
func (cf *ConvertConfig) RegisterAnyConvertFunc(t reflect.Type, convertFunc AnyConvertFunc) {
|
||||
cf.anyToTypeConvertMap[t] = convertFunc
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@ -8,48 +8,19 @@ package structcache
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/utils"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
// CommonConverter holds some converting functions of common types for internal usage.
|
||||
type CommonConverter struct {
|
||||
Int64 func(any interface{}) int64
|
||||
Uint64 func(any interface{}) uint64
|
||||
String func(any interface{}) string
|
||||
Float32 func(any interface{}) float32
|
||||
Float64 func(any interface{}) float64
|
||||
Time func(any interface{}, format ...string) time.Time
|
||||
GTime func(any interface{}, format ...string) *gtime.Time
|
||||
Bytes func(any interface{}) []byte
|
||||
Bool func(any interface{}) bool
|
||||
}
|
||||
|
||||
var (
|
||||
// map[reflect.Type]*CachedStructInfo
|
||||
cachedStructsInfoMap = sync.Map{}
|
||||
|
||||
// localCommonConverter holds some converting functions of common types for internal usage.
|
||||
localCommonConverter CommonConverter
|
||||
)
|
||||
|
||||
// RegisterCommonConverter registers the CommonConverter for local usage.
|
||||
func RegisterCommonConverter(commonConverter CommonConverter) {
|
||||
localCommonConverter = commonConverter
|
||||
}
|
||||
|
||||
// GetCachedStructInfo retrieves or parses and returns a cached info for certain struct type.
|
||||
// The given `structType` should be type of struct.
|
||||
func GetCachedStructInfo(structType reflect.Type, priorityTag string) *CachedStructInfo {
|
||||
func (cf *ConvertConfig) GetCachedStructInfo(structType reflect.Type, priorityTag string) *CachedStructInfo {
|
||||
if structType.Kind() != reflect.Struct {
|
||||
return nil
|
||||
}
|
||||
// check if it has been cached.
|
||||
cachedStructInfo, ok := getCachedConvertStructInfo(structType)
|
||||
cachedStructInfo, ok := cf.getCachedConvertStructInfo(structType)
|
||||
if ok {
|
||||
// directly returns the cached struct info if already exists.
|
||||
return cachedStructInfo
|
||||
@ -58,9 +29,7 @@ func GetCachedStructInfo(structType reflect.Type, priorityTag string) *CachedStr
|
||||
// else create one.
|
||||
|
||||
// it parses and generates a cache info for given struct type.
|
||||
cachedStructInfo = &CachedStructInfo{
|
||||
tagOrFiledNameToFieldInfoMap: make(map[string]*CachedFieldInfo),
|
||||
}
|
||||
cachedStructInfo = NewCachedStructInfo(cf.customConvertTypeMap, cf.anyToTypeConvertMap)
|
||||
var (
|
||||
priorityTagArray []string
|
||||
parentIndex = make([]int, 0)
|
||||
@ -70,19 +39,19 @@ func GetCachedStructInfo(structType reflect.Type, priorityTag string) *CachedStr
|
||||
} else {
|
||||
priorityTagArray = gtag.StructTagPriority
|
||||
}
|
||||
parseStructToCachedStructInfo(structType, parentIndex, cachedStructInfo, priorityTagArray)
|
||||
storeCachedStructInfo(structType, cachedStructInfo)
|
||||
cf.parseStructToCachedStructInfo(structType, parentIndex, cachedStructInfo, priorityTagArray)
|
||||
cf.storeCachedStructInfo(structType, cachedStructInfo)
|
||||
return cachedStructInfo
|
||||
}
|
||||
|
||||
func storeCachedStructInfo(structType reflect.Type, cachedStructInfo *CachedStructInfo) {
|
||||
func (cf *ConvertConfig) storeCachedStructInfo(structType reflect.Type, cachedStructInfo *CachedStructInfo) {
|
||||
// Temporarily enabled as an experimental feature
|
||||
cachedStructsInfoMap.Store(structType, cachedStructInfo)
|
||||
cf.cachedStructsInfoMap.Store(structType, cachedStructInfo)
|
||||
}
|
||||
|
||||
func getCachedConvertStructInfo(structType reflect.Type) (*CachedStructInfo, bool) {
|
||||
func (cf *ConvertConfig) getCachedConvertStructInfo(structType reflect.Type) (*CachedStructInfo, bool) {
|
||||
// Temporarily enabled as an experimental feature
|
||||
v, ok := cachedStructsInfoMap.Load(structType)
|
||||
v, ok := cf.cachedStructsInfoMap.Load(structType)
|
||||
if ok {
|
||||
return v.(*CachedStructInfo), ok
|
||||
}
|
||||
@ -91,7 +60,7 @@ func getCachedConvertStructInfo(structType reflect.Type) (*CachedStructInfo, boo
|
||||
|
||||
// parseStructToCachedStructInfo parses given struct reflection type and stores its fields info into given CachedStructInfo.
|
||||
// It stores nothing into CachedStructInfo if given struct reflection type has no fields.
|
||||
func parseStructToCachedStructInfo(
|
||||
func (cf *ConvertConfig) parseStructToCachedStructInfo(
|
||||
structType reflect.Type,
|
||||
fieldIndexes []int,
|
||||
cachedStructInfo *CachedStructInfo,
|
||||
@ -136,7 +105,7 @@ func parseStructToCachedStructInfo(
|
||||
// Do not add anonymous structures without tags
|
||||
cachedStructInfo.AddField(structField, append(copyFieldIndexes, i), priorityTagArray)
|
||||
}
|
||||
parseStructToCachedStructInfo(fieldType, append(copyFieldIndexes, i), cachedStructInfo, priorityTagArray)
|
||||
cf.parseStructToCachedStructInfo(fieldType, append(copyFieldIndexes, i), cachedStructInfo, priorityTagArray)
|
||||
continue
|
||||
}
|
||||
// Do not directly use append(fieldIndexes, i)
|
||||
|
||||
@ -76,7 +76,7 @@ type CachedFieldInfoBase struct {
|
||||
OtherSameNameField []*CachedFieldInfo
|
||||
|
||||
// ConvertFunc is the converting function for this field.
|
||||
ConvertFunc func(from any, to reflect.Value)
|
||||
ConvertFunc AnyConvertFunc
|
||||
|
||||
// The last fuzzy matching key for this field.
|
||||
// The fuzzy matching occurs only if there are no direct tag and field name matching in the params map.
|
||||
|
||||
@ -9,14 +9,24 @@ package structcache
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/utils"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// CachedStructInfo holds the cached info for certain struct.
|
||||
type CachedStructInfo struct {
|
||||
// All sub attributes field info slice.
|
||||
fieldConvertInfos []*CachedFieldInfo
|
||||
|
||||
// customConvertTypeMap is used to store whether field types are registered to custom conversions
|
||||
// For example:
|
||||
// func (src *TypeA) (dst *TypeB,err error)
|
||||
// This map will store `TypeB` for quick judgment during assignment.
|
||||
customConvertTypeMap map[reflect.Type]struct{}
|
||||
|
||||
// anyToTypeConvertMap for custom type converting from any to its reflect.Value.
|
||||
anyToTypeConvertMap map[reflect.Type]AnyConvertFunc
|
||||
|
||||
// This map field is mainly used in the bindStructWithLoopParamsMap method
|
||||
// key = field's name
|
||||
// Will save all field names and PriorityTagAndFieldName
|
||||
@ -25,9 +35,23 @@ type CachedStructInfo struct {
|
||||
//
|
||||
// It will be stored twice, which keys are `name` and `field`.
|
||||
tagOrFiledNameToFieldInfoMap map[string]*CachedFieldInfo
|
||||
}
|
||||
|
||||
// All sub attributes field info slice.
|
||||
FieldConvertInfos []*CachedFieldInfo
|
||||
// NewCachedStructInfo creates and returns a new CachedStructInfo object.
|
||||
func NewCachedStructInfo(
|
||||
customConvertTypeMap map[reflect.Type]struct{},
|
||||
anyToTypeConvertMap map[reflect.Type]AnyConvertFunc,
|
||||
) *CachedStructInfo {
|
||||
return &CachedStructInfo{
|
||||
tagOrFiledNameToFieldInfoMap: make(map[string]*CachedFieldInfo),
|
||||
fieldConvertInfos: make([]*CachedFieldInfo, 0),
|
||||
customConvertTypeMap: customConvertTypeMap,
|
||||
anyToTypeConvertMap: anyToTypeConvertMap,
|
||||
}
|
||||
}
|
||||
|
||||
func (csi *CachedStructInfo) GetFieldConvertInfos() []*CachedFieldInfo {
|
||||
return csi.fieldConvertInfos
|
||||
}
|
||||
|
||||
func (csi *CachedStructInfo) HasNoFields() bool {
|
||||
@ -46,7 +70,7 @@ func (csi *CachedStructInfo) AddField(field reflect.StructField, fieldIndexes []
|
||||
field, fieldIndexes, priorityTags, cachedFieldInfo, tagOrFieldName,
|
||||
)
|
||||
if newFieldInfo.IsField {
|
||||
csi.FieldConvertInfos = append(csi.FieldConvertInfos, newFieldInfo)
|
||||
csi.fieldConvertInfos = append(csi.fieldConvertInfos, newFieldInfo)
|
||||
}
|
||||
// if the field info by `tagOrFieldName` already cached,
|
||||
// it so adds this new field info to other same name field.
|
||||
@ -82,7 +106,9 @@ func (csi *CachedStructInfo) makeOrCopyCachedInfo(
|
||||
|
||||
// copyCachedInfoWithFieldIndexes copies and returns a new CachedFieldInfo based on given CachedFieldInfo, but different
|
||||
// FieldIndexes. Mainly used for copying fields with the same name and type.
|
||||
func (csi *CachedStructInfo) copyCachedInfoWithFieldIndexes(cfi *CachedFieldInfo, fieldIndexes []int) *CachedFieldInfo {
|
||||
func (csi *CachedStructInfo) copyCachedInfoWithFieldIndexes(
|
||||
cfi *CachedFieldInfo, fieldIndexes []int,
|
||||
) *CachedFieldInfo {
|
||||
base := CachedFieldInfoBase{}
|
||||
base = *cfi.CachedFieldInfoBase
|
||||
base.FieldIndexes = fieldIndexes
|
||||
@ -98,7 +124,7 @@ func (csi *CachedStructInfo) makeCachedFieldInfo(
|
||||
IsCommonInterface: checkTypeIsCommonInterface(field),
|
||||
StructField: field,
|
||||
FieldIndexes: fieldIndexes,
|
||||
ConvertFunc: csi.genFieldConvertFunc(field.Type.String()),
|
||||
ConvertFunc: csi.genFieldConvertFunc(field.Type),
|
||||
IsCustomConvert: csi.checkTypeHasCustomConvert(field.Type),
|
||||
PriorityTagAndFieldName: csi.genPriorityTagAndFieldName(field, priorityTags),
|
||||
RemoveSymbolsFieldName: utils.RemoveSymbols(field.Name),
|
||||
@ -109,59 +135,20 @@ func (csi *CachedStructInfo) makeCachedFieldInfo(
|
||||
}
|
||||
}
|
||||
|
||||
func (csi *CachedStructInfo) genFieldConvertFunc(fieldType string) (convertFunc func(from any, to reflect.Value)) {
|
||||
if fieldType[0] == '*' {
|
||||
convertFunc = csi.genFieldConvertFunc(fieldType[1:])
|
||||
func (csi *CachedStructInfo) genFieldConvertFunc(fieldType reflect.Type) (convertFunc AnyConvertFunc) {
|
||||
if v := csi.anyToTypeConvertMap[fieldType]; v != nil {
|
||||
return v
|
||||
}
|
||||
|
||||
var fieldTypeKind = fieldType.Kind()
|
||||
if fieldTypeKind == reflect.Ptr {
|
||||
convertFunc = csi.genFieldConvertFunc(fieldType.Elem())
|
||||
if convertFunc == nil {
|
||||
return nil
|
||||
}
|
||||
return csi.genPtrConvertFunc(convertFunc)
|
||||
}
|
||||
switch fieldType {
|
||||
case "int", "int8", "int16", "int32", "int64":
|
||||
convertFunc = func(from any, to reflect.Value) {
|
||||
to.SetInt(localCommonConverter.Int64(from))
|
||||
}
|
||||
case "uint", "uint8", "uint16", "uint32", "uint64":
|
||||
convertFunc = func(from any, to reflect.Value) {
|
||||
to.SetUint(localCommonConverter.Uint64(from))
|
||||
}
|
||||
case "string":
|
||||
convertFunc = func(from any, to reflect.Value) {
|
||||
to.SetString(localCommonConverter.String(from))
|
||||
}
|
||||
case "float32":
|
||||
convertFunc = func(from any, to reflect.Value) {
|
||||
to.SetFloat(float64(localCommonConverter.Float32(from)))
|
||||
}
|
||||
case "float64":
|
||||
convertFunc = func(from any, to reflect.Value) {
|
||||
to.SetFloat(localCommonConverter.Float64(from))
|
||||
}
|
||||
case "Time", "time.Time":
|
||||
convertFunc = func(from any, to reflect.Value) {
|
||||
*to.Addr().Interface().(*time.Time) = localCommonConverter.Time(from)
|
||||
}
|
||||
case "GTime", "gtime.Time":
|
||||
convertFunc = func(from any, to reflect.Value) {
|
||||
v := localCommonConverter.GTime(from)
|
||||
if v == nil {
|
||||
v = gtime.New()
|
||||
}
|
||||
*to.Addr().Interface().(*gtime.Time) = *v
|
||||
}
|
||||
case "bool":
|
||||
convertFunc = func(from any, to reflect.Value) {
|
||||
to.SetBool(localCommonConverter.Bool(from))
|
||||
}
|
||||
case "[]byte":
|
||||
convertFunc = func(from any, to reflect.Value) {
|
||||
to.SetBytes(localCommonConverter.Bytes(from))
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return convertFunc
|
||||
return nil
|
||||
}
|
||||
|
||||
func (csi *CachedStructInfo) genPriorityTagAndFieldName(
|
||||
@ -188,21 +175,19 @@ func (csi *CachedStructInfo) genPriorityTagAndFieldName(
|
||||
return
|
||||
}
|
||||
|
||||
func (csi *CachedStructInfo) genPtrConvertFunc(convertFunc AnyConvertFunc) AnyConvertFunc {
|
||||
return func(from any, to reflect.Value) error {
|
||||
if to.IsNil() {
|
||||
to.Set(reflect.New(to.Type().Elem()))
|
||||
}
|
||||
return convertFunc(from, to.Elem())
|
||||
}
|
||||
}
|
||||
|
||||
func (csi *CachedStructInfo) checkTypeHasCustomConvert(fieldType reflect.Type) bool {
|
||||
if fieldType.Kind() == reflect.Ptr {
|
||||
fieldType = fieldType.Elem()
|
||||
}
|
||||
_, ok := customConvertTypeMap[fieldType]
|
||||
_, ok := csi.customConvertTypeMap[fieldType]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (csi *CachedStructInfo) genPtrConvertFunc(
|
||||
convertFunc func(from any, to reflect.Value),
|
||||
) func(from any, to reflect.Value) {
|
||||
return func(from any, to reflect.Value) {
|
||||
if to.IsNil() {
|
||||
to.Set(reflect.New(to.Type().Elem()))
|
||||
}
|
||||
convertFunc(from, to.Elem())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user