This commit is contained in:
John Guo
2025-03-02 13:09:40 +08:00
parent 7f030248ac
commit d5e786ce93
35 changed files with 3214 additions and 3085 deletions

View File

@ -32,6 +32,17 @@ var (
type IUnmarshalValue = localinterface.IUnmarshalValue
var (
// defaultConvertConfig is the default configuration for type converting.
defaultConvertConfig = NewConvertConfig()
// defaultConverter is the default management object converting.
defaultConverter = NewConverter()
)
// RegisterConverter registers custom converter.
// Deprecated: use RegisterTypeConverterFunc instead for clear
func RegisterConverter(fn any) (err error) {
return defaultConverter.RegisterTypeConverterFunc(fn)
}
// RegisterTypeConverterFunc registers custom converter.
func RegisterTypeConverterFunc(fn any) (err error) {
return defaultConverter.RegisterTypeConverterFunc(fn)
}

View File

@ -6,309 +6,40 @@
package gconv
import (
"fmt"
"math"
"reflect"
"strconv"
"strings"
"time"
"github.com/gogf/gf/v2/encoding/gbinary"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/reflection"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
// Byte converts `any` to byte.
func Byte(any any) byte {
v, _ := doByte(any)
v, _ := defaultConverter.Uint8(any)
return v
}
func doByte(any any) (byte, error) {
if v, ok := any.(byte); ok {
return v, nil
}
return doUint8(any)
}
// Bytes converts `any` to []byte.
func Bytes(any any) []byte {
v, _ := doBytes(any)
v, _ := defaultConverter.Bytes(any)
return v
}
func doBytes(any any) ([]byte, error) {
if empty.IsNil(any) {
return nil, nil
}
switch value := any.(type) {
case string:
return []byte(value), nil
case []byte:
return value, nil
default:
if f, ok := value.(localinterface.IBytes); ok {
return f.Bytes(), nil
}
originValueAndKind := reflection.OriginValueAndKind(any)
switch originValueAndKind.OriginKind {
case reflect.Map:
bytes, err := json.Marshal(any)
if err != nil {
return nil, err
}
return bytes, nil
case reflect.Array, reflect.Slice:
var (
ok = true
bytes = make([]byte, originValueAndKind.OriginValue.Len())
)
for i := range bytes {
int32Value, err := doInt32(originValueAndKind.OriginValue.Index(i).Interface())
if err != nil {
return nil, err
}
if int32Value < 0 || int32Value > math.MaxUint8 {
ok = false
break
}
bytes[i] = byte(int32Value)
}
if ok {
return bytes, nil
}
default:
}
return gbinary.Encode(any), nil
}
}
// Rune converts `any` to rune.
func Rune(any any) rune {
v, _ := doRune(any)
v, _ := defaultConverter.Rune(any)
return v
}
func doRune(any any) (rune, error) {
if v, ok := any.(rune); ok {
return v, nil
}
v, err := doInt32(any)
if err != nil {
return 0, err
}
return v, nil
}
// Runes converts `any` to []rune.
func Runes(any any) []rune {
v, _ := doRunes(any)
v, _ := defaultConverter.Runes(any)
return v
}
func doRunes(any any) ([]rune, error) {
if v, ok := any.([]rune); ok {
return v, nil
}
s, err := doString(any)
if err != nil {
return nil, err
}
return []rune(s), nil
}
// String converts `any` to string.
// It's most commonly used converting function.
func String(any any) string {
v, _ := doString(any)
v, _ := defaultConverter.String(any)
return v
}
func doString(any any) (string, error) {
if empty.IsNil(any) {
return "", nil
}
switch value := any.(type) {
case int:
return strconv.Itoa(value), nil
case int8:
return strconv.Itoa(int(value)), nil
case int16:
return strconv.Itoa(int(value)), nil
case int32:
return strconv.Itoa(int(value)), nil
case int64:
return strconv.FormatInt(value, 10), nil
case uint:
return strconv.FormatUint(uint64(value), 10), nil
case uint8:
return strconv.FormatUint(uint64(value), 10), nil
case uint16:
return strconv.FormatUint(uint64(value), 10), nil
case uint32:
return strconv.FormatUint(uint64(value), 10), nil
case uint64:
return strconv.FormatUint(value, 10), nil
case float32:
return strconv.FormatFloat(float64(value), 'f', -1, 32), nil
case float64:
return strconv.FormatFloat(value, 'f', -1, 64), nil
case bool:
return strconv.FormatBool(value), nil
case string:
return value, nil
case []byte:
return string(value), nil
case complex64, complex128:
return fmt.Sprintf("%v", value), nil
case time.Time:
if value.IsZero() {
return "", nil
}
return value.String(), nil
case *time.Time:
if value == nil {
return "", nil
}
return value.String(), nil
case gtime.Time:
if value.IsZero() {
return "", nil
}
return value.String(), nil
case *gtime.Time:
if value == nil {
return "", nil
}
return value.String(), nil
default:
if f, ok := value.(localinterface.IString); ok {
// If the variable implements the String() interface,
// then use that interface to perform the conversion
return f.String(), nil
}
if f, ok := value.(localinterface.IError); ok {
// If the variable implements the Error() interface,
// then use that interface to perform the conversion
return f.Error(), nil
}
// Reflect checks.
var (
rv = reflect.ValueOf(value)
kind = rv.Kind()
)
switch kind {
case
reflect.Chan,
reflect.Map,
reflect.Slice,
reflect.Func,
reflect.Interface,
reflect.UnsafePointer:
if rv.IsNil() {
return "", nil
}
case reflect.String:
return rv.String(), nil
case reflect.Ptr:
if rv.IsNil() {
return "", nil
}
return doString(rv.Elem().Interface())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(rv.Int(), 10), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.FormatUint(rv.Uint(), 10), nil
case reflect.Uintptr:
return strconv.FormatUint(rv.Uint(), 10), nil
case reflect.Float32, reflect.Float64:
return strconv.FormatFloat(rv.Float(), 'f', -1, 64), nil
case reflect.Bool:
return strconv.FormatBool(rv.Bool()), nil
default:
}
// Finally, we use json.Marshal to convert.
jsonContent, err := json.Marshal(value)
if err != nil {
return fmt.Sprint(value), gerror.WrapCodef(
gcode.CodeInvalidParameter, err, "error marshaling value to JSON for: %v", value,
)
}
return string(jsonContent), nil
}
}
// Bool converts `any` to bool.
// It returns false if `any` is: false, "", 0, "false", "off", "no", empty slice/map.
func Bool(any any) bool {
v, _ := doBool(any)
v, _ := defaultConverter.Bool(any)
return v
}
func doBool(any any) (bool, error) {
if empty.IsNil(any) {
return false, nil
}
switch value := any.(type) {
case bool:
return value, nil
case []byte:
if _, ok := emptyStringMap[strings.ToLower(string(value))]; ok {
return false, nil
}
return true, nil
case string:
if _, ok := emptyStringMap[strings.ToLower(value)]; ok {
return false, nil
}
return true, nil
default:
if f, ok := value.(localinterface.IBool); ok {
return f.Bool(), nil
}
rv := reflect.ValueOf(any)
switch rv.Kind() {
case reflect.Ptr:
if rv.IsNil() {
return false, nil
}
if rv.Type().Elem().Kind() == reflect.Bool {
return rv.Elem().Bool(), nil
}
return doBool(rv.Elem().Interface())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return rv.Int() != 0, nil
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return rv.Uint() != 0, nil
case reflect.Float32, reflect.Float64:
return rv.Float() != 0, nil
case reflect.Bool:
return rv.Bool(), nil
// TODO(MapArraySliceStruct) It might panic here for these types.
case reflect.Map, reflect.Array:
fallthrough
case reflect.Slice:
return rv.Len() != 0, nil
case reflect.Struct:
return true, nil
default:
s, err := doString(any)
if err != nil {
return false, err
}
if _, ok := emptyStringMap[strings.ToLower(s)]; ok {
return false, nil
}
return true, nil
}
}
}

View File

@ -7,22 +7,15 @@
package gconv
import (
"context"
"reflect"
"time"
"github.com/gogf/gf/v2/internal/intlog"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/os/gtime"
)
// Convert converts the variable `fromValue` to the type `toTypeName`, the type `toTypeName` is specified by string.
//
// 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(
defaultConvertConfig,
func Convert(fromValue any, toTypeName string, extraParams ...any) any {
return defaultConverter.doConvert(
doConvertInput{
FromValue: fromValue,
ToTypeName: toTypeName,
@ -36,15 +29,14 @@ func Convert(fromValue interface{}, toTypeName string, extraParams ...interface{
//
// 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 ConvertWithRefer(fromValue interface{}, referValue interface{}, extraParams ...interface{}) interface{} {
func ConvertWithRefer(fromValue any, referValue any, extraParams ...any) any {
var referValueRf reflect.Value
if v, ok := referValue.(reflect.Value); ok {
referValueRf = v
} else {
referValueRf = reflect.ValueOf(referValue)
}
return doConvert(
defaultConvertConfig,
return defaultConverter.doConvert(
doConvertInput{
FromValue: fromValue,
ToTypeName: referValueRf.Type().String(),
@ -53,313 +45,3 @@ func ConvertWithRefer(fromValue interface{}, referValue interface{}, extraParams
},
)
}
type doConvertInput struct {
FromValue interface{} // Value that is converted from.
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(cf *ConvertConfig, in doConvertInput) (convertedValue interface{}) {
switch in.ToTypeName {
case "int":
return Int(in.FromValue)
case "*int":
if _, ok := in.FromValue.(*int); ok {
return in.FromValue
}
v := Int(in.FromValue)
return &v
case "int8":
return Int8(in.FromValue)
case "*int8":
if _, ok := in.FromValue.(*int8); ok {
return in.FromValue
}
v := Int8(in.FromValue)
return &v
case "int16":
return Int16(in.FromValue)
case "*int16":
if _, ok := in.FromValue.(*int16); ok {
return in.FromValue
}
v := Int16(in.FromValue)
return &v
case "int32":
return Int32(in.FromValue)
case "*int32":
if _, ok := in.FromValue.(*int32); ok {
return in.FromValue
}
v := Int32(in.FromValue)
return &v
case "int64":
return Int64(in.FromValue)
case "*int64":
if _, ok := in.FromValue.(*int64); ok {
return in.FromValue
}
v := Int64(in.FromValue)
return &v
case "uint":
return Uint(in.FromValue)
case "*uint":
if _, ok := in.FromValue.(*uint); ok {
return in.FromValue
}
v := Uint(in.FromValue)
return &v
case "uint8":
return Uint8(in.FromValue)
case "*uint8":
if _, ok := in.FromValue.(*uint8); ok {
return in.FromValue
}
v := Uint8(in.FromValue)
return &v
case "uint16":
return Uint16(in.FromValue)
case "*uint16":
if _, ok := in.FromValue.(*uint16); ok {
return in.FromValue
}
v := Uint16(in.FromValue)
return &v
case "uint32":
return Uint32(in.FromValue)
case "*uint32":
if _, ok := in.FromValue.(*uint32); ok {
return in.FromValue
}
v := Uint32(in.FromValue)
return &v
case "uint64":
return Uint64(in.FromValue)
case "*uint64":
if _, ok := in.FromValue.(*uint64); ok {
return in.FromValue
}
v := Uint64(in.FromValue)
return &v
case "float32":
return Float32(in.FromValue)
case "*float32":
if _, ok := in.FromValue.(*float32); ok {
return in.FromValue
}
v := Float32(in.FromValue)
return &v
case "float64":
return Float64(in.FromValue)
case "*float64":
if _, ok := in.FromValue.(*float64); ok {
return in.FromValue
}
v := Float64(in.FromValue)
return &v
case "bool":
return Bool(in.FromValue)
case "*bool":
if _, ok := in.FromValue.(*bool); ok {
return in.FromValue
}
v := Bool(in.FromValue)
return &v
case "string":
return String(in.FromValue)
case "*string":
if _, ok := in.FromValue.(*string); ok {
return in.FromValue
}
v := String(in.FromValue)
return &v
case "[]byte":
return Bytes(in.FromValue)
case "[]int":
return Ints(in.FromValue)
case "[]int32":
return Int32s(in.FromValue)
case "[]int64":
return Int64s(in.FromValue)
case "[]uint":
return Uints(in.FromValue)
case "[]uint8":
return Bytes(in.FromValue)
case "[]uint32":
return Uint32s(in.FromValue)
case "[]uint64":
return Uint64s(in.FromValue)
case "[]float32":
return Float32s(in.FromValue)
case "[]float64":
return Float64s(in.FromValue)
case "[]string":
return Strings(in.FromValue)
case "Time", "time.Time":
if len(in.Extra) > 0 {
return Time(in.FromValue, String(in.Extra[0]))
}
return Time(in.FromValue)
case "*time.Time":
var v time.Time
if len(in.Extra) > 0 {
v = Time(in.FromValue, String(in.Extra[0]))
} else {
if _, ok := in.FromValue.(*time.Time); ok {
return in.FromValue
}
v = Time(in.FromValue)
}
return &v
case "GTime", "gtime.Time":
if len(in.Extra) > 0 {
if v := GTime(in.FromValue, String(in.Extra[0])); v != nil {
return *v
} else {
return *gtime.New()
}
}
if v := GTime(in.FromValue); v != nil {
return *v
} else {
return *gtime.New()
}
case "*gtime.Time":
if len(in.Extra) > 0 {
if v := GTime(in.FromValue, String(in.Extra[0])); v != nil {
return v
} else {
return gtime.New()
}
}
if v := GTime(in.FromValue); v != nil {
return v
} else {
return gtime.New()
}
case "Duration", "time.Duration":
return Duration(in.FromValue)
case "*time.Duration":
if _, ok := in.FromValue.(*time.Duration); ok {
return in.FromValue
}
v := Duration(in.FromValue)
return &v
case "map[string]string":
return MapStrStr(in.FromValue)
case "map[string]interface {}":
return Map(in.FromValue)
case "[]map[string]interface {}":
return Maps(in.FromValue)
case "RawMessage", "json.RawMessage":
// issue 3449
bytes, err := json.Marshal(in.FromValue)
if err != nil {
intlog.Errorf(context.TODO(), `%+v`, err)
}
return bytes
default:
if in.ReferValue != nil {
var referReflectValue reflect.Value
if v, ok := in.ReferValue.(reflect.Value); ok {
referReflectValue = v
} else {
referReflectValue = reflect.ValueOf(in.ReferValue)
}
var fromReflectValue reflect.Value
if v, ok := in.FromValue.(reflect.Value); ok {
fromReflectValue = v
} else {
fromReflectValue = reflect.ValueOf(in.FromValue)
}
// custom converter.
if dstReflectValue, ok, _ := cf.callCustomConverterWithRefer(fromReflectValue, referReflectValue); ok {
return dstReflectValue.Interface()
}
defer func() {
if recover() != nil {
in.alreadySetToReferValue = false
if err := bindVarToReflectValue(cf, referReflectValue, in.FromValue, nil); err == nil {
in.alreadySetToReferValue = true
convertedValue = referReflectValue.Interface()
}
}
}()
switch referReflectValue.Kind() {
case reflect.Ptr:
// Type converting for custom type pointers.
// Eg:
// type PayMode int
// type Req struct{
// Mode *PayMode
// }
//
// Struct(`{"Mode": 1000}`, &req)
originType := referReflectValue.Type().Elem()
switch originType.Kind() {
case reflect.Struct:
// Not support some kinds.
default:
in.ToTypeName = originType.Kind().String()
in.ReferValue = nil
refElementValue := reflect.ValueOf(doConvert(cf, in))
originTypeValue := reflect.New(refElementValue.Type()).Elem()
originTypeValue.Set(refElementValue)
in.alreadySetToReferValue = true
return originTypeValue.Addr().Convert(referReflectValue.Type()).Interface()
}
case reflect.Map:
var targetValue = reflect.New(referReflectValue.Type()).Elem()
if err := doMapToMap(cf, in.FromValue, targetValue); err == nil {
in.alreadySetToReferValue = true
}
return targetValue.Interface()
}
in.ToTypeName = referReflectValue.Kind().String()
in.ReferValue = nil
in.alreadySetToReferValue = true
convertedValue = reflect.ValueOf(doConvert(cf, in)).Convert(referReflectValue.Type()).Interface()
return convertedValue
}
return in.FromValue
}
}
func doConvertWithReflectValueSet(cf *ConvertConfig, reflectValue reflect.Value, in doConvertInput) {
convertedValue := doConvert(cf, in)
if !in.alreadySetToReferValue {
reflectValue.Set(reflect.ValueOf(convertedValue))
}
}

View File

@ -1,99 +0,0 @@
// 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)
}
}

View File

@ -7,18 +7,99 @@
package gconv
import (
"context"
"reflect"
"time"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/intlog"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv/internal/structcache"
)
// RegisterConverter registers custom converter.
func RegisterConverter(fn any) (err error) {
return defaultConvertConfig.RegisterConverter(fn)
type (
converterInType = reflect.Type
converterOutType = reflect.Type
converterFunc = reflect.Value
)
// Converter is the manager for type converting.
type Converter struct {
internalConvertConfig *structcache.ConvertConfig
typeConverterFuncMap map[converterInType]map[converterOutType]converterFunc
}
// RegisterConverter registers custom converter.
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()
)
// NewConverter creates and returns management object for type converting.
func NewConverter() *Converter {
cf := &Converter{
internalConvertConfig: structcache.NewConvertConfig(),
typeConverterFuncMap: make(map[converterInType]map[converterOutType]converterFunc),
}
cf.registerBuiltInConverter()
return cf
}
func (c *Converter) registerBuiltInConverter() {
c.registerAnyConvertFuncForTypes(
c.builtInAnyConvertFuncForInt64, intType, int8Type, int16Type, int32Type, int64Type,
)
c.registerAnyConvertFuncForTypes(
c.builtInAnyConvertFuncForUint64, uintType, uint8Type, uint16Type, uint32Type, uint64Type,
)
c.registerAnyConvertFuncForTypes(
c.builtInAnyConvertFuncForString, stringType,
)
c.registerAnyConvertFuncForTypes(
c.builtInAnyConvertFuncForFloat64, float32Type, float64Type,
)
c.registerAnyConvertFuncForTypes(
c.builtInAnyConvertFuncForBool, boolType,
)
c.registerAnyConvertFuncForTypes(
c.builtInAnyConvertFuncForBytes, bytesType,
)
c.registerAnyConvertFuncForTypes(
c.builtInAnyConvertFuncForTime, timeType,
)
c.registerAnyConvertFuncForTypes(
c.builtInAnyConvertFuncForGTime, gtimeType,
)
}
func (c *Converter) registerAnyConvertFuncForTypes(convertFunc AnyConvertFunc, types ...reflect.Type) {
for _, t := range types {
c.internalConvertConfig.RegisterAnyConvertFunc(t, convertFunc)
}
}
// RegisterTypeConverterFunc 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.
//
@ -26,7 +107,7 @@ func RegisterConverter(fn any) (err error) {
// 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 (cf *ConvertConfig) RegisterConverter(fn any) (err error) {
func (c *Converter) RegisterTypeConverterFunc(fn any) (err error) {
var (
fnReflectType = reflect.TypeOf(fn)
errType = reflect.TypeOf((*error)(nil)).Elem()
@ -65,10 +146,10 @@ func (cf *ConvertConfig) RegisterConverter(fn any) (err error) {
return
}
registeredOutTypeMap, ok := cf.customConverters[inType]
registeredOutTypeMap, ok := c.typeConverterFuncMap[inType]
if !ok {
registeredOutTypeMap = make(map[converterOutType]converterFunc)
cf.customConverters[inType] = registeredOutTypeMap
c.typeConverterFuncMap[inType] = registeredOutTypeMap
}
if _, ok = registeredOutTypeMap[outType]; ok {
err = gerror.NewCodef(
@ -79,14 +160,14 @@ func (cf *ConvertConfig) RegisterConverter(fn any) (err error) {
return
}
registeredOutTypeMap[outType] = reflect.ValueOf(fn)
cf.internalConvertConfig.RegisterCustomConvertType(outType)
c.internalConvertConfig.RegisterTypeConvertFunc(outType)
return
}
func (cf *ConvertConfig) getRegisteredConverterFuncAndSrcType(
func (c *Converter) getRegisteredConverterFuncAndSrcType(
srcReflectValue, dstReflectValueForRefer reflect.Value,
) (f converterFunc, srcType reflect.Type, ok bool) {
if len(cf.customConverters) == 0 {
if len(c.typeConverterFuncMap) == 0 {
return reflect.Value{}, nil, false
}
srcType = srcReflectValue.Type()
@ -95,7 +176,7 @@ func (cf *ConvertConfig) getRegisteredConverterFuncAndSrcType(
}
var registeredOutTypeMap map[converterOutType]converterFunc
// firstly, it searches the map by input parameter type.
registeredOutTypeMap, ok = cf.customConverters[srcType]
registeredOutTypeMap, ok = c.typeConverterFuncMap[srcType]
if !ok {
return reflect.Value{}, nil, false
}
@ -119,28 +200,28 @@ func (cf *ConvertConfig) getRegisteredConverterFuncAndSrcType(
return
}
func (cf *ConvertConfig) callCustomConverterWithRefer(
func (c *Converter) callCustomConverterWithRefer(
srcReflectValue, referReflectValue reflect.Value,
) (dstReflectValue reflect.Value, converted bool, err error) {
registeredConverterFunc, srcType, ok := cf.getRegisteredConverterFuncAndSrcType(srcReflectValue, referReflectValue)
registeredConverterFunc, srcType, ok := c.getRegisteredConverterFuncAndSrcType(srcReflectValue, referReflectValue)
if !ok {
return reflect.Value{}, false, nil
}
dstReflectValue = reflect.New(referReflectValue.Type()).Elem()
converted, err = cf.doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType)
converted, err = c.doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType)
return
}
// callCustomConverter call the custom converter. It will try some possible type.
func (cf *ConvertConfig) callCustomConverter(srcReflectValue, dstReflectValue reflect.Value) (converted bool, err error) {
registeredConverterFunc, srcType, ok := cf.getRegisteredConverterFuncAndSrcType(srcReflectValue, dstReflectValue)
func (c *Converter) callCustomConverter(srcReflectValue, dstReflectValue reflect.Value) (converted bool, err error) {
registeredConverterFunc, srcType, ok := c.getRegisteredConverterFuncAndSrcType(srcReflectValue, dstReflectValue)
if !ok {
return false, nil
}
return cf.doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType)
return c.doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType)
}
func (cf *ConvertConfig) doCallCustomConverter(
func (c *Converter) doCallCustomConverter(
srcReflectValue reflect.Value,
dstReflectValue reflect.Value,
registeredConverterFunc converterFunc,
@ -181,3 +262,316 @@ func (cf *ConvertConfig) doCallCustomConverter(
return converted, nil
}
type doConvertInput struct {
FromValue interface{} // Value that is converted from.
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 (c *Converter) doConvert(in doConvertInput) (convertedValue interface{}) {
switch in.ToTypeName {
case "int":
return Int(in.FromValue)
case "*int":
if _, ok := in.FromValue.(*int); ok {
return in.FromValue
}
v := Int(in.FromValue)
return &v
case "int8":
return Int8(in.FromValue)
case "*int8":
if _, ok := in.FromValue.(*int8); ok {
return in.FromValue
}
v := Int8(in.FromValue)
return &v
case "int16":
return Int16(in.FromValue)
case "*int16":
if _, ok := in.FromValue.(*int16); ok {
return in.FromValue
}
v := Int16(in.FromValue)
return &v
case "int32":
return Int32(in.FromValue)
case "*int32":
if _, ok := in.FromValue.(*int32); ok {
return in.FromValue
}
v := Int32(in.FromValue)
return &v
case "int64":
return Int64(in.FromValue)
case "*int64":
if _, ok := in.FromValue.(*int64); ok {
return in.FromValue
}
v := Int64(in.FromValue)
return &v
case "uint":
return Uint(in.FromValue)
case "*uint":
if _, ok := in.FromValue.(*uint); ok {
return in.FromValue
}
v := Uint(in.FromValue)
return &v
case "uint8":
return Uint8(in.FromValue)
case "*uint8":
if _, ok := in.FromValue.(*uint8); ok {
return in.FromValue
}
v := Uint8(in.FromValue)
return &v
case "uint16":
return Uint16(in.FromValue)
case "*uint16":
if _, ok := in.FromValue.(*uint16); ok {
return in.FromValue
}
v := Uint16(in.FromValue)
return &v
case "uint32":
return Uint32(in.FromValue)
case "*uint32":
if _, ok := in.FromValue.(*uint32); ok {
return in.FromValue
}
v := Uint32(in.FromValue)
return &v
case "uint64":
return Uint64(in.FromValue)
case "*uint64":
if _, ok := in.FromValue.(*uint64); ok {
return in.FromValue
}
v := Uint64(in.FromValue)
return &v
case "float32":
return Float32(in.FromValue)
case "*float32":
if _, ok := in.FromValue.(*float32); ok {
return in.FromValue
}
v := Float32(in.FromValue)
return &v
case "float64":
return Float64(in.FromValue)
case "*float64":
if _, ok := in.FromValue.(*float64); ok {
return in.FromValue
}
v := Float64(in.FromValue)
return &v
case "bool":
return Bool(in.FromValue)
case "*bool":
if _, ok := in.FromValue.(*bool); ok {
return in.FromValue
}
v := Bool(in.FromValue)
return &v
case "string":
return String(in.FromValue)
case "*string":
if _, ok := in.FromValue.(*string); ok {
return in.FromValue
}
v := String(in.FromValue)
return &v
case "[]byte":
return Bytes(in.FromValue)
case "[]int":
return Ints(in.FromValue)
case "[]int32":
return Int32s(in.FromValue)
case "[]int64":
return Int64s(in.FromValue)
case "[]uint":
return Uints(in.FromValue)
case "[]uint8":
return Bytes(in.FromValue)
case "[]uint32":
return Uint32s(in.FromValue)
case "[]uint64":
return Uint64s(in.FromValue)
case "[]float32":
return Float32s(in.FromValue)
case "[]float64":
return Float64s(in.FromValue)
case "[]string":
return Strings(in.FromValue)
case "Time", "time.Time":
if len(in.Extra) > 0 {
return Time(in.FromValue, String(in.Extra[0]))
}
return Time(in.FromValue)
case "*time.Time":
var v time.Time
if len(in.Extra) > 0 {
v = Time(in.FromValue, String(in.Extra[0]))
} else {
if _, ok := in.FromValue.(*time.Time); ok {
return in.FromValue
}
v = Time(in.FromValue)
}
return &v
case "GTime", "gtime.Time":
if len(in.Extra) > 0 {
if v := GTime(in.FromValue, String(in.Extra[0])); v != nil {
return *v
} else {
return *gtime.New()
}
}
if v := GTime(in.FromValue); v != nil {
return *v
} else {
return *gtime.New()
}
case "*gtime.Time":
if len(in.Extra) > 0 {
if v := GTime(in.FromValue, String(in.Extra[0])); v != nil {
return v
} else {
return gtime.New()
}
}
if v := GTime(in.FromValue); v != nil {
return v
} else {
return gtime.New()
}
case "Duration", "time.Duration":
return Duration(in.FromValue)
case "*time.Duration":
if _, ok := in.FromValue.(*time.Duration); ok {
return in.FromValue
}
v := Duration(in.FromValue)
return &v
case "map[string]string":
return MapStrStr(in.FromValue)
case "map[string]interface {}":
return Map(in.FromValue)
case "[]map[string]interface {}":
return Maps(in.FromValue)
case "RawMessage", "json.RawMessage":
// issue 3449
bytes, err := json.Marshal(in.FromValue)
if err != nil {
intlog.Errorf(context.TODO(), `%+v`, err)
}
return bytes
default:
if in.ReferValue != nil {
var referReflectValue reflect.Value
if v, ok := in.ReferValue.(reflect.Value); ok {
referReflectValue = v
} else {
referReflectValue = reflect.ValueOf(in.ReferValue)
}
var fromReflectValue reflect.Value
if v, ok := in.FromValue.(reflect.Value); ok {
fromReflectValue = v
} else {
fromReflectValue = reflect.ValueOf(in.FromValue)
}
// custom converter.
if dstReflectValue, ok, _ := c.callCustomConverterWithRefer(fromReflectValue, referReflectValue); ok {
return dstReflectValue.Interface()
}
defer func() {
if recover() != nil {
in.alreadySetToReferValue = false
if err := c.bindVarToReflectValue(referReflectValue, in.FromValue, nil); err == nil {
in.alreadySetToReferValue = true
convertedValue = referReflectValue.Interface()
}
}
}()
switch referReflectValue.Kind() {
case reflect.Ptr:
// Type converting for custom type pointers.
// Eg:
// type PayMode int
// type Req struct{
// Mode *PayMode
// }
//
// Struct(`{"Mode": 1000}`, &req)
originType := referReflectValue.Type().Elem()
switch originType.Kind() {
case reflect.Struct:
// Not support some kinds.
default:
in.ToTypeName = originType.Kind().String()
in.ReferValue = nil
refElementValue := reflect.ValueOf(c.doConvert(in))
originTypeValue := reflect.New(refElementValue.Type()).Elem()
originTypeValue.Set(refElementValue)
in.alreadySetToReferValue = true
return originTypeValue.Addr().Convert(referReflectValue.Type()).Interface()
}
case reflect.Map:
var targetValue = reflect.New(referReflectValue.Type()).Elem()
if err := c.MapToMap(in.FromValue, targetValue); err == nil {
in.alreadySetToReferValue = true
}
return targetValue.Interface()
default:
}
in.ToTypeName = referReflectValue.Kind().String()
in.ReferValue = nil
in.alreadySetToReferValue = true
convertedValue = reflect.ValueOf(c.doConvert(in)).Convert(referReflectValue.Type()).Interface()
return convertedValue
}
return in.FromValue
}
}
func (c *Converter) doConvertWithReflectValueSet(reflectValue reflect.Value, in doConvertInput) {
convertedValue := c.doConvert(in)
if !in.alreadySetToReferValue {
reflectValue.Set(reflect.ValueOf(convertedValue))
}
}

View File

@ -0,0 +1,74 @@
// 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"
"strings"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
func (c *Converter) Bool(any any) (bool, error) {
if empty.IsNil(any) {
return false, nil
}
switch value := any.(type) {
case bool:
return value, nil
case []byte:
if _, ok := emptyStringMap[strings.ToLower(string(value))]; ok {
return false, nil
}
return true, nil
case string:
if _, ok := emptyStringMap[strings.ToLower(value)]; ok {
return false, nil
}
return true, nil
default:
if f, ok := value.(localinterface.IBool); ok {
return f.Bool(), nil
}
rv := reflect.ValueOf(any)
switch rv.Kind() {
case reflect.Ptr:
if rv.IsNil() {
return false, nil
}
if rv.Type().Elem().Kind() == reflect.Bool {
return rv.Elem().Bool(), nil
}
return c.Bool(rv.Elem().Interface())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return rv.Int() != 0, nil
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return rv.Uint() != 0, nil
case reflect.Float32, reflect.Float64:
return rv.Float() != 0, nil
case reflect.Bool:
return rv.Bool(), nil
// TODO(MapArraySliceStruct) It might panic here for these types.
case reflect.Map, reflect.Array:
fallthrough
case reflect.Slice:
return rv.Len() != 0, nil
case reflect.Struct:
return true, nil
default:
s, err := c.String(any)
if err != nil {
return false, err
}
if _, ok := emptyStringMap[strings.ToLower(s)]; ok {
return false, nil
}
return true, nil
}
}
}

View File

@ -13,8 +13,8 @@ import (
"github.com/gogf/gf/v2/os/gtime"
)
func builtInAnyConvertFuncForInt64(from any, to reflect.Value) error {
v, err := doInt64(from)
func (c *Converter) builtInAnyConvertFuncForInt64(from any, to reflect.Value) error {
v, err := c.Int64(from)
if err != nil {
return err
}
@ -22,8 +22,8 @@ func builtInAnyConvertFuncForInt64(from any, to reflect.Value) error {
return nil
}
func builtInAnyConvertFuncForUint64(from any, to reflect.Value) error {
v, err := doUint64(from)
func (c *Converter) builtInAnyConvertFuncForUint64(from any, to reflect.Value) error {
v, err := c.Uint64(from)
if err != nil {
return err
}
@ -31,8 +31,8 @@ func builtInAnyConvertFuncForUint64(from any, to reflect.Value) error {
return nil
}
func builtInAnyConvertFuncForString(from any, to reflect.Value) error {
v, err := doString(from)
func (c *Converter) builtInAnyConvertFuncForString(from any, to reflect.Value) error {
v, err := c.String(from)
if err != nil {
return err
}
@ -40,8 +40,8 @@ func builtInAnyConvertFuncForString(from any, to reflect.Value) error {
return nil
}
func builtInAnyConvertFuncForFloat64(from any, to reflect.Value) error {
v, err := doFloat64(from)
func (c *Converter) builtInAnyConvertFuncForFloat64(from any, to reflect.Value) error {
v, err := c.Float64(from)
if err != nil {
return err
}
@ -49,8 +49,8 @@ func builtInAnyConvertFuncForFloat64(from any, to reflect.Value) error {
return nil
}
func builtInAnyConvertFuncForBool(from any, to reflect.Value) error {
v, err := doBool(from)
func (c *Converter) builtInAnyConvertFuncForBool(from any, to reflect.Value) error {
v, err := c.Bool(from)
if err != nil {
return err
}
@ -58,8 +58,8 @@ func builtInAnyConvertFuncForBool(from any, to reflect.Value) error {
return nil
}
func builtInAnyConvertFuncForBytes(from any, to reflect.Value) error {
v, err := doBytes(from)
func (c *Converter) builtInAnyConvertFuncForBytes(from any, to reflect.Value) error {
v, err := c.Bytes(from)
if err != nil {
return err
}
@ -67,13 +67,20 @@ func builtInAnyConvertFuncForBytes(from any, to reflect.Value) error {
return nil
}
func builtInAnyConvertFuncForTime(from any, to reflect.Value) error {
*to.Addr().Interface().(*time.Time) = Time(from)
func (c *Converter) builtInAnyConvertFuncForTime(from any, to reflect.Value) error {
t, err := c.Time(from)
if err != nil {
return err
}
*to.Addr().Interface().(*time.Time) = t
return nil
}
func builtInAnyConvertFuncForGTime(from any, to reflect.Value) error {
v := GTime(from)
func (c *Converter) builtInAnyConvertFuncForGTime(from any, to reflect.Value) error {
v, err := c.GTime(from)
if err != nil {
return err
}
if v == nil {
v = gtime.New()
}

View File

@ -0,0 +1,67 @@
// 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 (
"math"
"reflect"
"github.com/gogf/gf/v2/encoding/gbinary"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/reflection"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
func (c *Converter) Bytes(any any) ([]byte, error) {
if empty.IsNil(any) {
return nil, nil
}
switch value := any.(type) {
case string:
return []byte(value), nil
case []byte:
return value, nil
default:
if f, ok := value.(localinterface.IBytes); ok {
return f.Bytes(), nil
}
originValueAndKind := reflection.OriginValueAndKind(any)
switch originValueAndKind.OriginKind {
case reflect.Map:
bytes, err := json.Marshal(any)
if err != nil {
return nil, err
}
return bytes, nil
case reflect.Array, reflect.Slice:
var (
ok = true
bytes = make([]byte, originValueAndKind.OriginValue.Len())
)
for i := range bytes {
int32Value, err := c.Int32(originValueAndKind.OriginValue.Index(i).Interface())
if err != nil {
return nil, err
}
if int32Value < 0 || int32Value > math.MaxUint8 {
ok = false
break
}
bytes[i] = byte(int32Value)
}
if ok {
return bytes, nil
}
default:
}
return gbinary.Encode(any), nil
}
}

View File

@ -0,0 +1,145 @@
// 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"
"strconv"
"github.com/gogf/gf/v2/encoding/gbinary"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
func (c *Converter) Float32(any any) (float32, error) {
if empty.IsNil(any) {
return 0, nil
}
switch value := any.(type) {
case float32:
return value, nil
case float64:
return float32(value), nil
case []byte:
// TODO: It might panic here for these types.
return gbinary.DecodeToFloat32(value), nil
default:
rv := reflect.ValueOf(any)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float32(rv.Int()), nil
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return float32(rv.Uint()), nil
case reflect.Float32, reflect.Float64:
return float32(rv.Float()), nil
case reflect.Bool:
if rv.Bool() {
return 1, nil
}
return 0, nil
case reflect.String:
f, err := strconv.ParseFloat(rv.String(), 32)
if err != nil {
return 0, gerror.WrapCodef(
gcode.CodeInvalidParameter, err, "converting string to float32 failed for: %v", any,
)
}
return float32(f), nil
case reflect.Ptr:
if rv.IsNil() {
return 0, nil
}
if f, ok := value.(localinterface.IFloat32); ok {
return f.Float32(), nil
}
return c.Float32(rv.Elem().Interface())
default:
if f, ok := value.(localinterface.IFloat32); ok {
return f.Float32(), nil
}
s, err := c.String(any)
if err != nil {
return 0, err
}
v, err := strconv.ParseFloat(s, 32)
if err != nil {
return 0, gerror.WrapCodef(
gcode.CodeInvalidParameter, err, "converting string to float32 failed for: %v", any,
)
}
return float32(v), nil
}
}
}
func (c *Converter) Float64(any any) (float64, error) {
if empty.IsNil(any) {
return 0, nil
}
switch value := any.(type) {
case float32:
return float64(value), nil
case float64:
return value, nil
case []byte:
// TODO: It might panic here for these types.
return gbinary.DecodeToFloat64(value), nil
default:
rv := reflect.ValueOf(any)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float64(rv.Int()), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return float64(rv.Uint()), nil
case reflect.Uintptr:
return float64(rv.Uint()), nil
case reflect.Float32, reflect.Float64:
// Please Note:
// When the type is float32 or a custom type defined based on float32,
// switching to float64 may result in a few extra decimal places.
return rv.Float(), nil
case reflect.Bool:
if rv.Bool() {
return 1, nil
}
return 0, nil
case reflect.String:
f, err := strconv.ParseFloat(rv.String(), 64)
if err != nil {
return 0, gerror.WrapCodef(
gcode.CodeInvalidParameter, err, "converting string to float64 failed for: %v", any,
)
}
return f, nil
case reflect.Ptr:
if rv.IsNil() {
return 0, nil
}
if f, ok := value.(localinterface.IFloat64); ok {
return f.Float64(), nil
}
return c.Float64(rv.Elem().Interface())
default:
if f, ok := value.(localinterface.IFloat64); ok {
return f.Float64(), nil
}
s, err := c.String(any)
if err != nil {
return 0, err
}
v, err := strconv.ParseFloat(s, 64)
if err != nil {
return 0, gerror.WrapCodef(
gcode.CodeInvalidParameter, err, "converting string to float64 failed for: %v", any,
)
}
return v, nil
}
}
}

View File

@ -0,0 +1,152 @@
// 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 (
"math"
"reflect"
"strconv"
"github.com/gogf/gf/v2/encoding/gbinary"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
func (c *Converter) Int(any any) (int, error) {
if v, ok := any.(int); ok {
return v, nil
}
v, err := c.Int64(any)
if err != nil {
return 0, err
}
return int(v), nil
}
func (c *Converter) Int8(any any) (int8, error) {
if v, ok := any.(int8); ok {
return v, nil
}
v, err := c.Int64(any)
if err != nil {
return 0, err
}
return int8(v), nil
}
func (c *Converter) Int16(any any) (int16, error) {
if v, ok := any.(int16); ok {
return v, nil
}
v, err := defaultConverter.Int64(any)
if err != nil {
return 0, err
}
return int16(v), nil
}
func (c *Converter) Int32(any any) (int32, error) {
if v, ok := any.(int32); ok {
return v, nil
}
v, err := c.Int64(any)
if err != nil {
return 0, err
}
return int32(v), nil
}
func (c *Converter) Int64(any any) (int64, error) {
if empty.IsNil(any) {
return 0, nil
}
if v, ok := any.(int64); ok {
return v, nil
}
rv := reflect.ValueOf(any)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return rv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return int64(rv.Uint()), nil
case reflect.Uintptr:
return int64(rv.Uint()), nil
case reflect.Float32, reflect.Float64:
return int64(rv.Float()), nil
case reflect.Bool:
if rv.Bool() {
return 1, nil
}
return 0, nil
case reflect.Ptr:
if rv.IsNil() {
return 0, nil
}
if f, ok := any.(localinterface.IInt64); ok {
return f.Int64(), nil
}
return c.Int64(rv.Elem().Interface())
case reflect.Slice:
// TODO: It might panic here for these types.
if rv.Type().Elem().Kind() == reflect.Uint8 {
return gbinary.DecodeToInt64(rv.Bytes()), nil
}
case reflect.String:
var (
s = rv.String()
isMinus = false
)
if len(s) > 0 {
if s[0] == '-' {
isMinus = true
s = s[1:]
} else if s[0] == '+' {
s = s[1:]
}
}
// Hexadecimal.
if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
if v, e := strconv.ParseInt(s[2:], 16, 64); e == nil {
if isMinus {
return -v, nil
}
return v, nil
}
}
// Decimal.
if v, e := strconv.ParseInt(s, 10, 64); e == nil {
if isMinus {
return -v, nil
}
return v, nil
}
// Float64.
valueInt64, err := c.Float64(s)
if err != nil {
return 0, err
}
if math.IsNaN(valueInt64) {
return 0, nil
} else {
if isMinus {
return -int64(valueInt64), nil
}
return int64(valueInt64), nil
}
default:
if f, ok := any.(localinterface.IInt64); ok {
return f.Int64(), nil
}
}
return 0, gerror.NewCodef(
gcode.CodeInvalidParameter,
`unsupport value type for converting to int64: %v`,
reflect.TypeOf(any),
)
}

View File

@ -0,0 +1,504 @@
// 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"
"strings"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
"github.com/gogf/gf/v2/util/gtag"
)
// MapConvert 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 (c *Converter) doMapConvert(value interface{}, recursive recursiveType, mustMapReturn bool, option ...MapOption) map[string]interface{} {
if value == nil {
return nil
}
// It redirects to its underlying value if it has implemented interface iVal.
if v, ok := value.(localinterface.IVal); ok {
value = v.Val()
}
var (
usedOption = getUsedMapOption(option...)
newTags = gtag.StructTagPriority
)
if usedOption.Deep {
recursive = recursiveTypeTrue
}
switch len(usedOption.Tags) {
case 0:
// No need handling.
case 1:
newTags = append(strings.Split(usedOption.Tags[0], ","), gtag.StructTagPriority...)
default:
newTags = append(usedOption.Tags, gtag.StructTagPriority...)
}
// Assert the common combination of types, and finally it uses reflection.
dataMap := make(map[string]interface{})
switch r := value.(type) {
case string:
// If it is a JSON string, automatically unmarshal it!
if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' {
if err := json.UnmarshalUseNumber([]byte(r), &dataMap); err != nil {
return nil
}
} else {
return nil
}
case []byte:
// If it is a JSON string, automatically unmarshal it!
if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' {
if err := json.UnmarshalUseNumber(r, &dataMap); err != nil {
return nil
}
} else {
return nil
}
case map[interface{}]interface{}:
recursiveOption := usedOption
recursiveOption.Tags = newTags
for k, v := range r {
dataMap[String(k)] = c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: v,
RecursiveType: recursive,
RecursiveOption: recursive == recursiveTypeTrue,
Option: recursiveOption,
},
)
}
case map[interface{}]string:
for k, v := range r {
dataMap[String(k)] = v
}
case map[interface{}]int:
for k, v := range r {
dataMap[String(k)] = v
}
case map[interface{}]uint:
for k, v := range r {
dataMap[String(k)] = v
}
case map[interface{}]float32:
for k, v := range r {
dataMap[String(k)] = v
}
case map[interface{}]float64:
for k, v := range r {
dataMap[String(k)] = v
}
case map[string]bool:
for k, v := range r {
dataMap[k] = v
}
case map[string]int:
for k, v := range r {
dataMap[k] = v
}
case map[string]uint:
for k, v := range r {
dataMap[k] = v
}
case map[string]float32:
for k, v := range r {
dataMap[k] = v
}
case map[string]float64:
for k, v := range r {
dataMap[k] = v
}
case map[string]string:
for k, v := range r {
dataMap[k] = v
}
case map[string]interface{}:
if recursive == recursiveTypeTrue {
recursiveOption := usedOption
recursiveOption.Tags = newTags
// A copy of current map.
for k, v := range r {
dataMap[k] = c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: v,
RecursiveType: recursive,
RecursiveOption: recursive == recursiveTypeTrue,
Option: recursiveOption,
},
)
}
} else {
// It returns the map directly without any changing.
return r
}
case map[int]interface{}:
recursiveOption := usedOption
recursiveOption.Tags = newTags
for k, v := range r {
dataMap[String(k)] = c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: v,
RecursiveType: recursive,
RecursiveOption: recursive == recursiveTypeTrue,
Option: recursiveOption,
},
)
}
case map[int]string:
for k, v := range r {
dataMap[String(k)] = v
}
case map[uint]string:
for k, v := range r {
dataMap[String(k)] = v
}
default:
// Not a common type, it then uses reflection for conversion.
var reflectValue reflect.Value
if v, ok := value.(reflect.Value); ok {
reflectValue = v
} else {
reflectValue = reflect.ValueOf(value)
}
reflectKind := reflectValue.Kind()
// If it is a pointer, we should find its real data type.
for reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
}
switch reflectKind {
// If `value` is type of array, it converts the value of even number index as its key and
// the value of odd number index as its corresponding value, for example:
// []string{"k1","v1","k2","v2"} => map[string]interface{}{"k1":"v1", "k2":"v2"}
// []string{"k1","v1","k2"} => map[string]interface{}{"k1":"v1", "k2":nil}
case reflect.Slice, reflect.Array:
length := reflectValue.Len()
for i := 0; i < length; i += 2 {
if i+1 < length {
dataMap[String(reflectValue.Index(i).Interface())] = reflectValue.Index(i + 1).Interface()
} else {
dataMap[String(reflectValue.Index(i).Interface())] = nil
}
}
case reflect.Map, reflect.Struct, reflect.Interface:
recursiveOption := usedOption
recursiveOption.Tags = newTags
convertedValue := c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: true,
Value: value,
RecursiveType: recursive,
RecursiveOption: recursive == recursiveTypeTrue,
Option: recursiveOption,
MustMapReturn: mustMapReturn,
},
)
if m, ok := convertedValue.(map[string]interface{}); ok {
return m
}
return nil
default:
return nil
}
}
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.
Option MapOption // Map converting option.
MustMapReturn bool // Must return map instead of Value when empty.
}
func (c *Converter) doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) interface{} {
if !in.IsRoot && !in.RecursiveOption {
return in.Value
}
var reflectValue reflect.Value
if v, ok := in.Value.(reflect.Value); ok {
reflectValue = v
in.Value = v.Interface()
} else {
reflectValue = reflect.ValueOf(in.Value)
}
reflectKind := reflectValue.Kind()
// If it is a pointer, we should find its real data type.
for reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
}
switch reflectKind {
case reflect.Map:
var (
mapIter = reflectValue.MapRange()
dataMap = make(map[string]interface{})
)
for mapIter.Next() {
var (
mapKeyValue = mapIter.Value()
mapValue interface{}
)
switch {
case mapKeyValue.IsZero():
if utils.CanCallIsNil(mapKeyValue) && mapKeyValue.IsNil() {
// quick check for nil value.
mapValue = nil
} else {
// in case of:
// exception recovered: reflect: call of reflect.Value.Interface on zero Value
mapValue = reflect.New(mapKeyValue.Type()).Elem().Interface()
}
default:
mapValue = mapKeyValue.Interface()
}
dataMap[String(mapIter.Key().Interface())] = c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: mapValue,
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Option: in.Option,
},
)
}
return dataMap
case reflect.Struct:
var dataMap = make(map[string]interface{})
// Map converting interface check.
if v, ok := in.Value.(localinterface.IMapStrAny); ok {
// Value copy, in case of concurrent safety.
for mapK, mapV := range v.MapStrAny() {
if in.RecursiveOption {
dataMap[mapK] = c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: mapV,
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Option: in.Option,
},
)
} else {
dataMap[mapK] = mapV
}
}
if len(dataMap) > 0 {
return dataMap
}
}
// Using reflect for converting.
var (
rtField reflect.StructField
rvField reflect.Value
reflectType = reflectValue.Type() // attribute value type.
mapKey = "" // mapKey may be the tag name or the struct attribute name.
)
for i := 0; i < reflectValue.NumField(); i++ {
rtField = reflectType.Field(i)
rvField = reflectValue.Field(i)
// Only convert the public attributes.
fieldName := rtField.Name
if !utils.IsLetterUpper(fieldName[0]) {
continue
}
mapKey = ""
fieldTag := rtField.Tag
for _, tag := range in.Option.Tags {
if mapKey = fieldTag.Get(tag); mapKey != "" {
break
}
}
if mapKey == "" {
mapKey = fieldName
} else {
// Support json tag feature: -, omitempty
mapKey = strings.TrimSpace(mapKey)
if mapKey == "-" {
continue
}
array := strings.Split(mapKey, ",")
if len(array) > 1 {
switch strings.TrimSpace(array[1]) {
case "omitempty":
if in.Option.OmitEmpty && empty.IsEmpty(rvField.Interface()) {
continue
} else {
mapKey = strings.TrimSpace(array[0])
}
default:
mapKey = strings.TrimSpace(array[0])
}
}
if mapKey == "" {
mapKey = fieldName
}
}
if in.RecursiveOption || rtField.Anonymous {
// Do map converting recursively.
var (
rvAttrField = rvField
rvAttrKind = rvField.Kind()
)
if rvAttrKind == reflect.Ptr {
rvAttrField = rvField.Elem()
rvAttrKind = rvAttrField.Kind()
}
switch rvAttrKind {
case reflect.Struct:
// Embedded struct and has no fields, just ignores it.
// Eg: gmeta.Meta
if rvAttrField.Type().NumField() == 0 {
continue
}
var (
hasNoTag = mapKey == fieldName
// DO NOT use rvAttrField.Interface() here,
// as it might be changed from pointer to struct.
rvInterface = rvField.Interface()
)
switch {
case hasNoTag && rtField.Anonymous:
// It means this attribute field has no tag.
// Overwrite the attribute with sub-struct attribute fields.
anonymousValue := c.doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: rvInterface,
RecursiveType: in.RecursiveType,
RecursiveOption: true,
Option: in.Option,
})
if m, ok := anonymousValue.(map[string]interface{}); ok {
for k, v := range m {
dataMap[k] = v
}
} else {
dataMap[mapKey] = rvInterface
}
// It means this attribute field has desired tag.
case !hasNoTag && rtField.Anonymous:
dataMap[mapKey] = c.doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: rvInterface,
RecursiveType: in.RecursiveType,
RecursiveOption: true,
Option: in.Option,
})
default:
dataMap[mapKey] = c.doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: rvInterface,
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Option: in.Option,
})
}
// The struct attribute is type of slice.
case reflect.Array, reflect.Slice:
length := rvAttrField.Len()
if length == 0 {
dataMap[mapKey] = rvAttrField.Interface()
break
}
array := make([]interface{}, length)
for arrayIndex := 0; arrayIndex < length; arrayIndex++ {
array[arrayIndex] = c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: rvAttrField.Index(arrayIndex).Interface(),
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Option: in.Option,
},
)
}
dataMap[mapKey] = array
case reflect.Map:
var (
mapIter = rvAttrField.MapRange()
nestedMap = make(map[string]interface{})
)
for mapIter.Next() {
nestedMap[String(mapIter.Key().Interface())] = c.doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: mapIter.Value().Interface(),
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Option: in.Option,
},
)
}
dataMap[mapKey] = nestedMap
default:
if rvField.IsValid() {
dataMap[mapKey] = reflectValue.Field(i).Interface()
} else {
dataMap[mapKey] = nil
}
}
} else {
// No recursive map value converting
if rvField.IsValid() {
dataMap[mapKey] = reflectValue.Field(i).Interface()
} else {
dataMap[mapKey] = nil
}
}
}
if !in.MustMapReturn && len(dataMap) == 0 {
return in.Value
}
return dataMap
// The given value is type of slice.
case reflect.Array, reflect.Slice:
length := reflectValue.Len()
if length == 0 {
break
}
array := make([]interface{}, reflectValue.Len())
for i := 0; i < length; i++ {
array[i] = c.doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: reflectValue.Index(i).Interface(),
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Option: in.Option,
})
}
return array
default:
}
return in.Value
}

View File

@ -0,0 +1,7 @@
// 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

View File

@ -0,0 +1,126 @@
// 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"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
)
// MapToMap converts any map type variable `params` to another map type variable `pointer`.
//
// The parameter `params` can be any type of map, like:
// map[string]string, map[string]struct, map[string]*struct, reflect.Value, etc.
//
// The parameter `pointer` should be type of *map, like:
// map[int]string, map[string]struct, map[string]*struct, reflect.Value, etc.
//
// 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 (c *Converter) MapToMap(params any, pointer any, mapping ...map[string]string) (err error) {
var (
paramsRv reflect.Value
paramsKind reflect.Kind
keyToAttributeNameMapping map[string]string
)
if len(mapping) > 0 {
keyToAttributeNameMapping = mapping[0]
}
if v, ok := params.(reflect.Value); ok {
paramsRv = v
} else {
paramsRv = reflect.ValueOf(params)
}
paramsKind = paramsRv.Kind()
if paramsKind == reflect.Ptr {
paramsRv = paramsRv.Elem()
paramsKind = paramsRv.Kind()
}
if paramsKind != reflect.Map {
return c.MapToMap(Map(params), pointer, mapping...)
}
// Empty params map, no need continue.
if paramsRv.Len() == 0 {
return nil
}
var pointerRv reflect.Value
if v, ok := pointer.(reflect.Value); ok {
pointerRv = v
} else {
pointerRv = reflect.ValueOf(pointer)
}
pointerKind := pointerRv.Kind()
for pointerKind == reflect.Ptr {
pointerRv = pointerRv.Elem()
pointerKind = pointerRv.Kind()
}
if pointerKind != reflect.Map {
return gerror.NewCodef(
gcode.CodeInvalidParameter,
`destination pointer should be type of *map, but got: %s`,
pointerKind,
)
}
defer func() {
// Catch the panic, especially the reflection operation panics.
if exception := recover(); exception != nil {
if v, ok := exception.(error); ok && gerror.HasStack(v) {
err = v
} else {
err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception)
}
}
}()
var (
paramsKeys = paramsRv.MapKeys()
pointerKeyType = pointerRv.Type().Key()
pointerValueType = pointerRv.Type().Elem()
pointerValueKind = pointerValueType.Kind()
dataMap = reflect.MakeMapWithSize(pointerRv.Type(), len(paramsKeys))
)
// Retrieve the true element type of target map.
if pointerValueKind == reflect.Ptr {
pointerValueKind = pointerValueType.Elem().Kind()
}
for _, key := range paramsKeys {
mapValue := reflect.New(pointerValueType).Elem()
switch pointerValueKind {
case reflect.Map, reflect.Struct:
if err = c.Struct(
paramsRv.MapIndex(key).Interface(), mapValue, keyToAttributeNameMapping, "",
); err != nil {
return err
}
default:
mapValue.Set(
reflect.ValueOf(
c.doConvert(doConvertInput{
FromValue: paramsRv.MapIndex(key).Interface(),
ToTypeName: pointerValueType.String(),
ReferValue: mapValue,
Extra: nil,
}),
),
)
}
var mapKey = reflect.ValueOf(
c.doConvert(
doConvertInput{
FromValue: key.Interface(),
ToTypeName: pointerKeyType.Name(),
ReferValue: reflect.New(pointerKeyType).Elem().Interface(),
Extra: nil,
},
),
)
dataMap.SetMapIndex(mapKey, mapValue)
}
pointerRv.Set(dataMap)
return nil
}

View File

@ -0,0 +1,120 @@
// 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"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
)
// MapToMaps converts any map type variable `params` to another map slice variable `pointer`.
//
// The parameter `params` can be type of []map, []*map, []struct, []*struct.
//
// The parameter `pointer` should be type of []map, []*map.
//
// The optional parameter `mapping` is used for struct attribute to map key mapping, which makes
// sense only if the item of `params` is type struct.
func (c *Converter) MapToMaps(params any, pointer any, paramKeyToAttrMap ...map[string]string) (err error) {
// Params and its element type check.
var (
paramsRv reflect.Value
paramsKind reflect.Kind
)
if v, ok := params.(reflect.Value); ok {
paramsRv = v
} else {
paramsRv = reflect.ValueOf(params)
}
paramsKind = paramsRv.Kind()
if paramsKind == reflect.Ptr {
paramsRv = paramsRv.Elem()
paramsKind = paramsRv.Kind()
}
if paramsKind != reflect.Array && paramsKind != reflect.Slice {
return gerror.NewCode(
gcode.CodeInvalidParameter,
"params should be type of slice, example: []map/[]*map/[]struct/[]*struct",
)
}
var (
paramsElem = paramsRv.Type().Elem()
paramsElemKind = paramsElem.Kind()
)
if paramsElemKind == reflect.Ptr {
paramsElem = paramsElem.Elem()
paramsElemKind = paramsElem.Kind()
}
if paramsElemKind != reflect.Map &&
paramsElemKind != reflect.Struct &&
paramsElemKind != reflect.Interface {
return gerror.NewCodef(
gcode.CodeInvalidParameter,
"params element should be type of map/*map/struct/*struct, but got: %s",
paramsElemKind,
)
}
// Empty slice, no need continue.
if paramsRv.Len() == 0 {
return nil
}
// Pointer and its element type check.
var (
pointerRv = reflect.ValueOf(pointer)
pointerKind = pointerRv.Kind()
)
for pointerKind == reflect.Ptr {
pointerRv = pointerRv.Elem()
pointerKind = pointerRv.Kind()
}
if pointerKind != reflect.Array && pointerKind != reflect.Slice {
return gerror.NewCode(gcode.CodeInvalidParameter, "pointer should be type of *[]map/*[]*map")
}
var (
pointerElemType = pointerRv.Type().Elem()
pointerElemKind = pointerElemType.Kind()
)
if pointerElemKind == reflect.Ptr {
pointerElemKind = pointerElemType.Elem().Kind()
}
if pointerElemKind != reflect.Map {
return gerror.NewCode(gcode.CodeInvalidParameter, "pointer element should be type of map/*map")
}
defer func() {
// Catch the panic, especially the reflection operation panics.
if exception := recover(); exception != nil {
if v, ok := exception.(error); ok && gerror.HasStack(v) {
err = v
} else {
err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception)
}
}
}()
var (
pointerSlice = reflect.MakeSlice(pointerRv.Type(), paramsRv.Len(), paramsRv.Len())
)
for i := 0; i < paramsRv.Len(); i++ {
var item reflect.Value
if pointerElemType.Kind() == reflect.Ptr {
item = reflect.New(pointerElemType.Elem())
if err = MapToMap(paramsRv.Index(i).Interface(), item, paramKeyToAttrMap...); err != nil {
return err
}
pointerSlice.Index(i).Set(item)
} else {
item = reflect.New(pointerElemType)
if err = MapToMap(paramsRv.Index(i).Interface(), item, paramKeyToAttrMap...); err != nil {
return err
}
pointerSlice.Index(i).Set(item.Elem())
}
}
pointerRv.Set(pointerSlice)
return
}

View File

@ -0,0 +1,29 @@
// 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
func (c *Converter) Rune(any any) (rune, error) {
if v, ok := any.(rune); ok {
return v, nil
}
v, err := c.Int32(any)
if err != nil {
return 0, err
}
return v, nil
}
func (c *Converter) Runes(any any) ([]rune, error) {
if v, ok := any.([]rune); ok {
return v, nil
}
s, err := c.String(any)
if err != nil {
return nil, err
}
return []rune(s), nil
}

View File

@ -0,0 +1,336 @@
// 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"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
func (c *Converter) Scan(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
}
// Check if dstPointer is nil, which is an invalid parameter
if dstPointer == nil {
return gerror.NewCode(
gcode.CodeInvalidParameter,
`destination pointer should not be nil`,
)
}
// Get the reflection type and value of dstPointer
var (
dstPointerReflectType reflect.Type
dstPointerReflectValue reflect.Value
)
if v, ok := dstPointer.(reflect.Value); ok {
dstPointerReflectValue = v
dstPointerReflectType = v.Type()
} else {
dstPointerReflectValue = reflect.ValueOf(dstPointer)
// Do not use dstPointerReflectValue.Type() as dstPointerReflectValue might be zero
dstPointerReflectType = reflect.TypeOf(dstPointer)
}
// Validate the kind of dstPointer
var dstPointerReflectKind = dstPointerReflectType.Kind()
if dstPointerReflectKind != reflect.Ptr {
// If dstPointer is not a pointer, try to get its address
if dstPointerReflectValue.CanAddr() {
dstPointerReflectValue = dstPointerReflectValue.Addr()
dstPointerReflectType = dstPointerReflectValue.Type()
dstPointerReflectKind = dstPointerReflectType.Kind()
} else {
// If dstPointer is not a pointer and cannot be addressed, return an error
return gerror.NewCodef(
gcode.CodeInvalidParameter,
`destination pointer should be type of pointer, but got type: %v`,
dstPointerReflectType,
)
}
}
// Get the reflection value of srcValue
var srcValueReflectValue reflect.Value
if v, ok := srcValue.(reflect.Value); ok {
srcValueReflectValue = v
} else {
srcValueReflectValue = reflect.ValueOf(srcValue)
}
// Get the element type and kind of dstPointer
var (
dstPointerReflectValueElem = dstPointerReflectValue.Elem()
dstPointerReflectValueElemKind = dstPointerReflectValueElem.Kind()
)
// Handle multiple level pointers
if dstPointerReflectValueElemKind == reflect.Ptr {
if dstPointerReflectValueElem.IsNil() {
// Create a new value for the pointer dereference
nextLevelPtr := reflect.New(dstPointerReflectValueElem.Type().Elem())
// Recursively scan into the dereferenced pointer
if err = Scan(srcValueReflectValue, nextLevelPtr, paramKeyToAttrMap...); err == nil {
dstPointerReflectValueElem.Set(nextLevelPtr)
}
return
}
return Scan(srcValueReflectValue, dstPointerReflectValueElem, paramKeyToAttrMap...)
}
// Check if srcValue and dstPointer are the same type, in which case direct assignment can be performed
if ok := doConvertWithTypeCheck(srcValueReflectValue, dstPointerReflectValueElem); ok {
return nil
}
// Handle different destination types
switch dstPointerReflectValueElemKind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
// Convert to int type
dstPointerReflectValueElem.SetInt(Int64(srcValue))
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
// Convert to uint type
dstPointerReflectValueElem.SetUint(Uint64(srcValue))
return nil
case reflect.Float32, reflect.Float64:
// Convert to float type
dstPointerReflectValueElem.SetFloat(Float64(srcValue))
return nil
case reflect.String:
// Convert to string type
dstPointerReflectValueElem.SetString(String(srcValue))
return nil
case reflect.Bool:
// Convert to bool type
dstPointerReflectValueElem.SetBool(Bool(srcValue))
return nil
case reflect.Slice:
// Handle slice type conversion
var (
dstElemType = dstPointerReflectValueElem.Type().Elem()
dstElemKind = dstElemType.Kind()
)
// The slice element might be a pointer type
if dstElemKind == reflect.Ptr {
dstElemType = dstElemType.Elem()
dstElemKind = dstElemType.Kind()
}
// Special handling for struct or map slice elements
if dstElemKind == reflect.Struct || dstElemKind == reflect.Map {
return c.doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...)
}
// Handle basic type slice conversions
var srcValueReflectValueKind = srcValueReflectValue.Kind()
if srcValueReflectValueKind == reflect.Slice || srcValueReflectValueKind == reflect.Array {
var (
srcLen = srcValueReflectValue.Len()
newSlice = reflect.MakeSlice(dstPointerReflectValueElem.Type(), srcLen, srcLen)
)
for i := 0; i < srcLen; i++ {
srcElem := srcValueReflectValue.Index(i).Interface()
switch dstElemType.Kind() {
case reflect.String:
newSlice.Index(i).SetString(String(srcElem))
case reflect.Int:
newSlice.Index(i).SetInt(Int64(srcElem))
case reflect.Int64:
newSlice.Index(i).SetInt(Int64(srcElem))
case reflect.Float64:
newSlice.Index(i).SetFloat(Float64(srcElem))
case reflect.Bool:
newSlice.Index(i).SetBool(Bool(srcElem))
default:
return Scan(
srcElem, newSlice.Index(i).Addr().Interface(), paramKeyToAttrMap...,
)
}
}
dstPointerReflectValueElem.Set(newSlice)
return nil
}
return c.doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...)
default:
// Handle complex types (structs, maps, etc.)
return c.doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...)
}
}
// doScanForComplicatedTypes handles the scanning of complex data types.
// It supports converting between maps, structs, and slices of these types.
// The function first attempts JSON conversion, then falls back to specific type handling.
//
// It supports `pointer` in type of `*map/*[]map/*[]*map/*struct/**struct/*[]struct/*[]*struct` for converting.
//
// Parameters:
// - srcValue: The source value to convert from
// - dstPointer: The destination pointer to convert to
// - dstPointerReflectType: The reflection type of the destination pointer
// - paramKeyToAttrMap: Optional mapping between parameter keys and struct attribute names
func (c *Converter) doScanForComplicatedTypes(
srcValue, dstPointer any,
dstPointerReflectType reflect.Type,
paramKeyToAttrMap ...map[string]string,
) error {
// Try JSON conversion first
ok, err := doConvertWithJsonCheck(srcValue, dstPointer)
if err != nil {
return err
}
if ok {
return nil
}
// Handle specific type conversions
var (
dstPointerReflectTypeElem = dstPointerReflectType.Elem()
dstPointerReflectTypeElemKind = dstPointerReflectTypeElem.Kind()
keyToAttributeNameMapping map[string]string
)
if len(paramKeyToAttrMap) > 0 {
keyToAttributeNameMapping = paramKeyToAttrMap[0]
}
// Handle different destination types
switch dstPointerReflectTypeElemKind {
case reflect.Map:
// Convert map to map
return c.MapToMap(srcValue, dstPointer, paramKeyToAttrMap...)
case reflect.Array, reflect.Slice:
var (
sliceElem = dstPointerReflectTypeElem.Elem()
sliceElemKind = sliceElem.Kind()
)
// Handle pointer elements
for sliceElemKind == reflect.Ptr {
sliceElem = sliceElem.Elem()
sliceElemKind = sliceElem.Kind()
}
if sliceElemKind == reflect.Map {
// Convert to slice of maps
return c.MapToMaps(srcValue, dstPointer, paramKeyToAttrMap...)
}
// Convert to slice of structs
return c.Structs(srcValue, dstPointer, keyToAttributeNameMapping, "")
default:
// Convert to single struct
return c.Struct(srcValue, dstPointer, keyToAttributeNameMapping, "")
}
}
// doConvertWithTypeCheck supports `pointer` in type of `*map/*[]map/*[]*map/*struct/**struct/*[]struct/*[]*struct`
// for converting.
func doConvertWithTypeCheck(srcValueReflectValue, dstPointerReflectValueElem reflect.Value) (ok bool) {
if !dstPointerReflectValueElem.IsValid() || !srcValueReflectValue.IsValid() {
return false
}
switch {
// Examples:
// UploadFile => UploadFile
// []UploadFile => []UploadFile
// *UploadFile => *UploadFile
// *[]UploadFile => *[]UploadFile
// map[int][int] => map[int][int]
// []map[int][int] => []map[int][int]
// *[]map[int][int] => *[]map[int][int]
case dstPointerReflectValueElem.Type() == srcValueReflectValue.Type():
dstPointerReflectValueElem.Set(srcValueReflectValue)
return true
// Examples:
// UploadFile => *UploadFile
// []UploadFile => *[]UploadFile
// map[int][int] => *map[int][int]
// []map[int][int] => *[]map[int][int]
case dstPointerReflectValueElem.Kind() == reflect.Ptr &&
dstPointerReflectValueElem.Elem().IsValid() &&
dstPointerReflectValueElem.Elem().Type() == srcValueReflectValue.Type():
dstPointerReflectValueElem.Elem().Set(srcValueReflectValue)
return true
// Examples:
// *UploadFile => UploadFile
// *[]UploadFile => []UploadFile
// *map[int][int] => map[int][int]
// *[]map[int][int] => []map[int][int]
case srcValueReflectValue.Kind() == reflect.Ptr &&
srcValueReflectValue.Elem().IsValid() &&
dstPointerReflectValueElem.Type() == srcValueReflectValue.Elem().Type():
dstPointerReflectValueElem.Set(srcValueReflectValue.Elem())
return true
default:
return false
}
}
// doConvertWithJsonCheck attempts to convert the source value to the destination
// using JSON marshaling and unmarshaling. This is particularly useful for complex
// types that can be represented as JSON.
//
// Parameters:
// - srcValue: The source value to convert from
// - dstPointer: The destination pointer to convert to
//
// Returns:
// - bool: true if JSON conversion was successful
// - error: any error that occurred during conversion
func doConvertWithJsonCheck(srcValue any, dstPointer any) (ok bool, err error) {
switch valueResult := srcValue.(type) {
case []byte:
if json.Valid(valueResult) {
if dstPointerReflectType, ok := dstPointer.(reflect.Value); ok {
if dstPointerReflectType.Kind() == reflect.Ptr {
if dstPointerReflectType.IsNil() {
return false, nil
}
return true, json.UnmarshalUseNumber(valueResult, dstPointerReflectType.Interface())
} else if dstPointerReflectType.CanAddr() {
return true, json.UnmarshalUseNumber(valueResult, dstPointerReflectType.Addr().Interface())
}
} else {
return true, json.UnmarshalUseNumber(valueResult, dstPointer)
}
}
case string:
if valueBytes := []byte(valueResult); json.Valid(valueBytes) {
if dstPointerReflectType, ok := dstPointer.(reflect.Value); ok {
if dstPointerReflectType.Kind() == reflect.Ptr {
if dstPointerReflectType.IsNil() {
return false, nil
}
return true, json.UnmarshalUseNumber(valueBytes, dstPointerReflectType.Interface())
} else if dstPointerReflectType.CanAddr() {
return true, json.UnmarshalUseNumber(valueBytes, dstPointerReflectType.Addr().Interface())
}
} else {
return true, json.UnmarshalUseNumber(valueBytes, dstPointer)
}
}
default:
// The `params` might be struct that implements interface function Interface, eg: gvar.Var.
if v, ok := srcValue.(localinterface.IInterface); ok {
return doConvertWithJsonCheck(v.Interface(), dstPointer)
}
}
return false, nil
}

View File

@ -0,0 +1,135 @@
// 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 (
"fmt"
"reflect"
"strconv"
"time"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
func (c *Converter) String(any any) (string, error) {
if empty.IsNil(any) {
return "", nil
}
switch value := any.(type) {
case int:
return strconv.Itoa(value), nil
case int8:
return strconv.Itoa(int(value)), nil
case int16:
return strconv.Itoa(int(value)), nil
case int32:
return strconv.Itoa(int(value)), nil
case int64:
return strconv.FormatInt(value, 10), nil
case uint:
return strconv.FormatUint(uint64(value), 10), nil
case uint8:
return strconv.FormatUint(uint64(value), 10), nil
case uint16:
return strconv.FormatUint(uint64(value), 10), nil
case uint32:
return strconv.FormatUint(uint64(value), 10), nil
case uint64:
return strconv.FormatUint(value, 10), nil
case float32:
return strconv.FormatFloat(float64(value), 'f', -1, 32), nil
case float64:
return strconv.FormatFloat(value, 'f', -1, 64), nil
case bool:
return strconv.FormatBool(value), nil
case string:
return value, nil
case []byte:
return string(value), nil
case complex64, complex128:
return fmt.Sprintf("%v", value), nil
case time.Time:
if value.IsZero() {
return "", nil
}
return value.String(), nil
case *time.Time:
if value == nil {
return "", nil
}
return value.String(), nil
case gtime.Time:
if value.IsZero() {
return "", nil
}
return value.String(), nil
case *gtime.Time:
if value == nil {
return "", nil
}
return value.String(), nil
default:
if f, ok := value.(localinterface.IString); ok {
// If the variable implements the String() interface,
// then use that interface to perform the conversion
return f.String(), nil
}
if f, ok := value.(localinterface.IError); ok {
// If the variable implements the Error() interface,
// then use that interface to perform the conversion
return f.Error(), nil
}
// Reflect checks.
var (
rv = reflect.ValueOf(value)
kind = rv.Kind()
)
switch kind {
case
reflect.Chan,
reflect.Map,
reflect.Slice,
reflect.Func,
reflect.Interface,
reflect.UnsafePointer:
if rv.IsNil() {
return "", nil
}
case reflect.String:
return rv.String(), nil
case reflect.Ptr:
if rv.IsNil() {
return "", nil
}
return c.String(rv.Elem().Interface())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(rv.Int(), 10), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.FormatUint(rv.Uint(), 10), nil
case reflect.Uintptr:
return strconv.FormatUint(rv.Uint(), 10), nil
case reflect.Float32, reflect.Float64:
return strconv.FormatFloat(rv.Float(), 'f', -1, 64), nil
case reflect.Bool:
return strconv.FormatBool(rv.Bool()), nil
default:
}
// Finally, we use json.Marshal to convert.
jsonContent, err := json.Marshal(value)
if err != nil {
return fmt.Sprint(value), gerror.WrapCodef(
gcode.CodeInvalidParameter, err, "error marshaling value to JSON for: %v", value,
)
}
return string(jsonContent), nil
}
}

View File

@ -0,0 +1,628 @@
// 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"
"strings"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
"github.com/gogf/gf/v2/util/gconv/internal/structcache"
)
// Struct is the core internal converting function for any data to struct.
func (c *Converter) Struct(
params any,
pointer any,
paramKeyToAttrMap map[string]string,
priorityTag string,
) (err error) {
if params == nil {
// If `params` is nil, no conversion.
return nil
}
if pointer == nil {
return gerror.NewCode(gcode.CodeInvalidParameter, "object pointer cannot be nil")
}
// JSON content converting.
ok, err := doConvertWithJsonCheck(params, pointer)
if err != nil {
return err
}
if ok {
return nil
}
defer func() {
// Catch the panic, especially the reflection operation panics.
if exception := recover(); exception != nil {
if v, ok := exception.(error); ok && gerror.HasStack(v) {
err = v
} else {
err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception)
}
}
}()
var (
paramsReflectValue 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.
)
if v, ok := params.(reflect.Value); ok {
paramsReflectValue = v
} else {
paramsReflectValue = reflect.ValueOf(params)
}
paramsInterface = paramsReflectValue.Interface()
if v, ok := pointer.(reflect.Value); ok {
pointerReflectValue = v
pointerElemReflectValue = v
} else {
pointerReflectValue = reflect.ValueOf(pointer)
pointerReflectKind = pointerReflectValue.Kind()
if pointerReflectKind != reflect.Ptr {
return gerror.NewCodef(
gcode.CodeInvalidParameter,
"destination pointer should be type of '*struct', but got '%v'",
pointerReflectKind,
)
}
// Using IsNil on reflect.Ptr variable is OK.
if !pointerReflectValue.IsValid() || pointerReflectValue.IsNil() {
return gerror.NewCode(
gcode.CodeInvalidParameter,
"destination pointer cannot be nil",
)
}
pointerElemReflectValue = pointerReflectValue.Elem()
}
// If `params` and `pointer` are the same type, the do directly assignment.
// For performance enhancement purpose.
if ok = doConvertWithTypeCheck(paramsReflectValue, pointerElemReflectValue); ok {
return nil
}
// custom convert.
if ok, err = c.callCustomConverter(paramsReflectValue, pointerReflectValue); ok {
return err
}
// Normal unmarshalling interfaces checks.
if ok, err = bindVarToReflectValueWithInterfaceCheck(pointerReflectValue, paramsInterface); ok {
return err
}
// It automatically creates struct object if necessary.
// For example, if `pointer` is **User, then `elem` is *User, which is a pointer to User.
if pointerElemReflectValue.Kind() == reflect.Ptr {
if !pointerElemReflectValue.IsValid() || pointerElemReflectValue.IsNil() {
e := reflect.New(pointerElemReflectValue.Type().Elem())
pointerElemReflectValue.Set(e)
defer func() {
if err != nil {
// If it is converted failed, it reset the `pointer` to nil.
pointerReflectValue.Elem().Set(reflect.Zero(pointerReflectValue.Type().Elem()))
}
}()
}
// if v, ok := pointerElemReflectValue.Interface().(localinterface.IUnmarshalValue); ok {
// return v.UnmarshalValue(params)
// }
// Note that it's `pointerElemReflectValue` here not `pointerReflectValue`.
if ok, err = bindVarToReflectValueWithInterfaceCheck(pointerElemReflectValue, paramsInterface); ok {
return err
}
// Retrieve its element, may be struct at last.
pointerElemReflectValue = pointerElemReflectValue.Elem()
}
paramsMap, ok := paramsInterface.(map[string]any)
if !ok {
// paramsMap is the map[string]any type variable for params.
// DO NOT use MapDeep here.
paramsMap = c.doMapConvert(paramsInterface, recursiveTypeAuto, true)
if paramsMap == nil {
return gerror.NewCodef(
gcode.CodeInvalidParameter,
`convert params from "%#v" to "map[string]any" failed`,
params,
)
}
}
// Nothing to be done as the parameters are empty.
if len(paramsMap) == 0 {
return nil
}
// Get struct info from cache or parse struct and cache the struct info.
cachedStructInfo := c.internalConvertConfig.GetCachedStructInfo(
pointerElemReflectValue.Type(), priorityTag,
)
// Nothing to be converted.
if cachedStructInfo == nil {
return nil
}
// For the structure types of 0 tagOrFiledNameToFieldInfoMap,
// they also need to be cached to prevent invalid logic
if cachedStructInfo.HasNoFields() {
return nil
}
var (
// Indicates that those values have been used and cannot be reused.
usedParamsKeyOrTagNameMap = structcache.GetUsedParamsKeyOrTagNameMapFromPool()
cachedFieldInfo *structcache.CachedFieldInfo
paramsValue any
)
defer structcache.PutUsedParamsKeyOrTagNameMapToPool(usedParamsKeyOrTagNameMap)
// Firstly, search according to custom mapping rules.
// If a possible direct assignment is found, reduce the number of subsequent map searches.
for paramKey, fieldName := range paramKeyToAttrMap {
paramsValue, ok = paramsMap[paramKey]
if !ok {
continue
}
cachedFieldInfo = cachedStructInfo.GetFieldInfo(fieldName)
if cachedFieldInfo != nil {
fieldValue := cachedFieldInfo.GetFieldReflectValueFrom(pointerElemReflectValue)
if err = c.bindVarToStructField(
cachedFieldInfo,
fieldValue,
paramsValue,
paramKeyToAttrMap,
); err != nil {
return err
}
if len(cachedFieldInfo.OtherSameNameField) > 0 {
if err = c.setOtherSameNameField(
cachedFieldInfo, paramsValue, pointerReflectValue, paramKeyToAttrMap,
); err != nil {
return err
}
}
usedParamsKeyOrTagNameMap[paramKey] = struct{}{}
}
}
// Already done converting for given `paramsMap`.
if len(usedParamsKeyOrTagNameMap) == len(paramsMap) {
return nil
}
return c.bindStructWithLoopFieldInfos(
paramsMap, pointerElemReflectValue, paramKeyToAttrMap, usedParamsKeyOrTagNameMap, cachedStructInfo,
)
}
func (c *Converter) setOtherSameNameField(
cachedFieldInfo *structcache.CachedFieldInfo,
srcValue any,
structValue reflect.Value,
paramKeyToAttrMap map[string]string,
) (err error) {
// loop the same field name of all sub attributes.
for _, otherFieldInfo := range cachedFieldInfo.OtherSameNameField {
fieldValue := cachedFieldInfo.GetOtherFieldReflectValueFrom(structValue, otherFieldInfo.FieldIndexes)
if err = c.bindVarToStructField(otherFieldInfo, fieldValue, srcValue, paramKeyToAttrMap); err != nil {
return err
}
}
return nil
}
func (c *Converter) bindStructWithLoopFieldInfos(
paramsMap map[string]any,
structValue reflect.Value,
paramKeyToAttrMap map[string]string,
usedParamsKeyOrTagNameMap map[string]struct{},
cachedStructInfo *structcache.CachedStructInfo,
) (err error) {
var (
cachedFieldInfo *structcache.CachedFieldInfo
fuzzLastKey string
fieldValue reflect.Value
paramKey string
paramValue any
matched bool
ok bool
)
for _, cachedFieldInfo = range cachedStructInfo.GetFieldConvertInfos() {
for _, fieldTag := range cachedFieldInfo.PriorityTagAndFieldName {
if paramValue, ok = paramsMap[fieldTag]; !ok {
continue
}
fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue)
if err = c.bindVarToStructField(
cachedFieldInfo, fieldValue, paramValue, paramKeyToAttrMap,
); err != nil {
return err
}
// handle same field name in nested struct.
if len(cachedFieldInfo.OtherSameNameField) > 0 {
if err = c.setOtherSameNameField(
cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap,
); err != nil {
return err
}
}
usedParamsKeyOrTagNameMap[fieldTag] = struct{}{}
matched = true
break
}
if matched {
matched = false
continue
}
fuzzLastKey = cachedFieldInfo.LastFuzzyKey.Load().(string)
if paramValue, ok = paramsMap[fuzzLastKey]; !ok {
paramKey, paramValue = fuzzyMatchingFieldName(
cachedFieldInfo.RemoveSymbolsFieldName, paramsMap, usedParamsKeyOrTagNameMap,
)
ok = paramKey != ""
cachedFieldInfo.LastFuzzyKey.Store(paramKey)
}
if ok {
fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue)
if paramValue != nil {
if err = c.bindVarToStructField(
cachedFieldInfo, fieldValue, paramValue, paramKeyToAttrMap,
); err != nil {
return err
}
// handle same field name in nested struct.
if len(cachedFieldInfo.OtherSameNameField) > 0 {
if err = c.setOtherSameNameField(
cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap,
); err != nil {
return err
}
}
}
usedParamsKeyOrTagNameMap[paramKey] = struct{}{}
}
}
return nil
}
// fuzzy matching rule:
// to match field name and param key in case-insensitive and without symbols.
func fuzzyMatchingFieldName(
fieldName string,
paramsMap map[string]any,
usedParamsKeyMap map[string]struct{},
) (string, any) {
for paramKey, paramValue := range paramsMap {
if _, ok := usedParamsKeyMap[paramKey]; ok {
continue
}
removeParamKeyUnderline := utils.RemoveSymbols(paramKey)
if strings.EqualFold(fieldName, removeParamKeyUnderline) {
return paramKey, paramValue
}
}
return "", nil
}
// bindVarToStructField sets value to struct object attribute by name.
// each value to attribute converting comes into in this function.
func (c *Converter) bindVarToStructField(
cachedFieldInfo *structcache.CachedFieldInfo,
fieldValue reflect.Value,
srcValue any,
paramKeyToAttrMap map[string]string,
) (err error) {
if !fieldValue.IsValid() {
return nil
}
// CanSet checks whether attribute is public accessible.
if !fieldValue.CanSet() {
return nil
}
defer func() {
if exception := recover(); exception != nil {
if err = c.bindVarToReflectValue(fieldValue, srcValue, paramKeyToAttrMap); err != nil {
err = gerror.Wrapf(err, `error binding srcValue to attribute "%s"`, cachedFieldInfo.FieldName())
}
}
}()
// Directly converting.
if empty.IsNil(srcValue) {
fieldValue.Set(reflect.Zero(fieldValue.Type()))
return nil
}
// Try to call custom converter.
// Issue: https://github.com/gogf/gf/issues/3099
var (
customConverterInput reflect.Value
ok bool
)
if cachedFieldInfo.IsCustomConvert {
if customConverterInput, ok = srcValue.(reflect.Value); !ok {
customConverterInput = reflect.ValueOf(srcValue)
}
if ok, err = c.callCustomConverter(customConverterInput, fieldValue); ok || err != nil {
return
}
}
if cachedFieldInfo.IsCommonInterface {
if ok, err = bindVarToReflectValueWithInterfaceCheck(fieldValue, srcValue); ok || err != nil {
return
}
}
// Common types use fast assignment logic
if cachedFieldInfo.ConvertFunc != nil {
return cachedFieldInfo.ConvertFunc(srcValue, fieldValue)
}
c.doConvertWithReflectValueSet(
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 any) (bool, error) {
var pointer any
if reflectValue.Kind() != reflect.Ptr && reflectValue.CanAddr() {
reflectValueAddr := reflectValue.Addr()
if reflectValueAddr.IsNil() || !reflectValueAddr.IsValid() {
return false, nil
}
// Not a pointer, but can token address, that makes it can be unmarshalled.
pointer = reflectValue.Addr().Interface()
} else {
if reflectValue.IsNil() || !reflectValue.IsValid() {
return false, nil
}
pointer = reflectValue.Interface()
}
// UnmarshalValue.
if v, ok := pointer.(localinterface.IUnmarshalValue); ok {
return ok, v.UnmarshalValue(value)
}
// UnmarshalText.
if v, ok := pointer.(localinterface.IUnmarshalText); ok {
var valueBytes []byte
if b, ok := value.([]byte); ok {
valueBytes = b
} else if s, ok := value.(string); ok {
valueBytes = []byte(s)
} else if f, ok := value.(localinterface.IString); ok {
valueBytes = []byte(f.String())
}
if len(valueBytes) > 0 {
return ok, v.UnmarshalText(valueBytes)
}
}
// UnmarshalJSON.
if v, ok := pointer.(localinterface.IUnmarshalJSON); ok {
var valueBytes []byte
if b, ok := value.([]byte); ok {
valueBytes = b
} else if s, ok := value.(string); ok {
valueBytes = []byte(s)
} else if f, ok := value.(localinterface.IString); ok {
valueBytes = []byte(f.String())
}
if len(valueBytes) > 0 {
// If it is not a valid JSON string, it then adds char `"` on its both sides to make it is.
if !json.Valid(valueBytes) {
newValueBytes := make([]byte, len(valueBytes)+2)
newValueBytes[0] = '"'
newValueBytes[len(newValueBytes)-1] = '"'
copy(newValueBytes[1:], valueBytes)
valueBytes = newValueBytes
}
return ok, v.UnmarshalJSON(valueBytes)
}
}
if v, ok := pointer.(localinterface.ISet); ok {
v.Set(value)
return ok, nil
}
return false, nil
}
// bindVarToReflectValue sets `value` to reflect value object `structFieldValue`.
func (c *Converter) bindVarToReflectValue(
structFieldValue reflect.Value, value any, paramKeyToAttrMap map[string]string,
) (err error) {
// JSON content converting.
ok, err := doConvertWithJsonCheck(value, structFieldValue)
if err != nil {
return err
}
if ok {
return nil
}
kind := structFieldValue.Kind()
// Converting using `Set` interface implements, for some types.
switch kind {
case reflect.Slice, reflect.Array, reflect.Ptr, reflect.Interface:
if !structFieldValue.IsNil() {
if v, ok := structFieldValue.Interface().(localinterface.ISet); ok {
v.Set(value)
return nil
}
}
default:
}
// Converting using reflection by kind.
switch kind {
case reflect.Map:
return c.MapToMap(value, structFieldValue, paramKeyToAttrMap)
case reflect.Struct:
// Recursively converting for struct attribute.
if err = c.Struct(value, structFieldValue, nil, ""); err != nil {
// Note there's reflect conversion mechanism here.
structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
}
// Note that the slice element might be type of struct,
// so it uses Struct function doing the converting internally.
case reflect.Slice, reflect.Array:
var (
reflectArray reflect.Value
reflectValue = reflect.ValueOf(value)
)
if reflectValue.Kind() == reflect.Slice || reflectValue.Kind() == reflect.Array {
reflectArray = reflect.MakeSlice(structFieldValue.Type(), reflectValue.Len(), reflectValue.Len())
if reflectValue.Len() > 0 {
var (
elemType = reflectArray.Index(0).Type()
elemTypeName string
converted bool
)
for i := 0; i < reflectValue.Len(); i++ {
converted = false
elemTypeName = elemType.Name()
if elemTypeName == "" {
elemTypeName = elemType.String()
}
var elem reflect.Value
if elemType.Kind() == reflect.Ptr {
elem = reflect.New(elemType.Elem()).Elem()
} else {
elem = reflect.New(elemType).Elem()
}
if elem.Kind() == reflect.Struct {
if err = c.Struct(reflectValue.Index(i).Interface(), elem, nil, ""); err == nil {
converted = true
}
}
if !converted {
c.doConvertWithReflectValueSet(
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.
elem = elem.Addr()
}
reflectArray.Index(i).Set(elem)
}
}
} else {
var (
elem reflect.Value
elemType = structFieldValue.Type().Elem()
elemTypeName = elemType.Name()
converted bool
)
switch reflectValue.Kind() {
case reflect.String:
// Value is empty string.
if reflectValue.IsZero() {
var elemKind = elemType.Kind()
// Try to find the original type kind of the slice element.
if elemKind == reflect.Ptr {
elemKind = elemType.Elem().Kind()
}
switch elemKind {
case reflect.String:
// Empty string cannot be assigned to string slice.
return nil
default:
}
}
default:
}
if elemTypeName == "" {
elemTypeName = elemType.String()
}
if elemType.Kind() == reflect.Ptr {
elem = reflect.New(elemType.Elem()).Elem()
} else {
elem = reflect.New(elemType).Elem()
}
if elem.Kind() == reflect.Struct {
if err = c.Struct(value, elem, nil, ""); err == nil {
converted = true
}
}
if !converted {
c.doConvertWithReflectValueSet(
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.
elem = elem.Addr()
}
reflectArray = reflect.MakeSlice(structFieldValue.Type(), 1, 1)
reflectArray.Index(0).Set(elem)
}
structFieldValue.Set(reflectArray)
case reflect.Ptr:
if structFieldValue.IsNil() || structFieldValue.IsZero() {
// Nil or empty pointer, it creates a new one.
item := reflect.New(structFieldValue.Type().Elem())
if ok, err = bindVarToReflectValueWithInterfaceCheck(item, value); ok {
structFieldValue.Set(item)
return err
}
elem := item.Elem()
if err = c.bindVarToReflectValue(elem, value, paramKeyToAttrMap); err == nil {
structFieldValue.Set(elem.Addr())
}
} else {
// Not empty pointer, it assigns values to it.
return c.bindVarToReflectValue(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((*any)(nil)))
} else {
// Note there's reflect conversion mechanism here.
structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
}
default:
defer func() {
if exception := recover(); exception != nil {
err = gerror.NewCodef(
gcode.CodeInternalPanic,
`cannot convert value "%+v" to type "%s":%+v`,
value,
structFieldValue.Type().String(),
exception,
)
}
}()
// It here uses reflect converting `value` to type of the attribute and assigns
// the result value to the attribute. It might fail and panic if the usual Go
// conversion rules do not allow conversion.
structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
}
return nil
}

View File

@ -0,0 +1,115 @@
// 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"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
)
// Structs converts any slice to given struct slice.
//
// It automatically checks and converts json string to []map if `params` is string/[]byte.
//
// The parameter `pointer` should be type of pointer to slice of struct.
// Note that if `pointer` is a pointer to another pointer of type of slice of struct,
// it will create the struct/pointer internally.
func (c *Converter) Structs(
params any, pointer any, paramKeyToAttrMap map[string]string, priorityTag string,
) (err error) {
defer func() {
// Catch the panic, especially the reflection operation panics.
if exception := recover(); exception != nil {
if v, ok := exception.(error); ok && gerror.HasStack(v) {
err = v
} else {
err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception)
}
}
}()
// Pointer type check.
pointerRv, ok := pointer.(reflect.Value)
if !ok {
pointerRv = reflect.ValueOf(pointer)
if kind := pointerRv.Kind(); kind != reflect.Ptr {
return gerror.NewCodef(
gcode.CodeInvalidParameter,
"pointer should be type of pointer, but got: %v", kind,
)
}
}
// Converting `params` to map slice.
var (
paramsList []any
paramsRv = reflect.ValueOf(params)
paramsKind = paramsRv.Kind()
)
for paramsKind == reflect.Ptr {
paramsRv = paramsRv.Elem()
paramsKind = paramsRv.Kind()
}
switch paramsKind {
case reflect.Slice, reflect.Array:
paramsList = make([]any, paramsRv.Len())
for i := 0; i < paramsRv.Len(); i++ {
paramsList[i] = paramsRv.Index(i).Interface()
}
default:
var paramsMaps = Maps(params)
paramsList = make([]any, len(paramsMaps))
for i := 0; i < len(paramsMaps); i++ {
paramsList[i] = paramsMaps[i]
}
}
// If `params` is an empty slice, no conversion.
if len(paramsList) == 0 {
return nil
}
var (
reflectElemArray = reflect.MakeSlice(pointerRv.Type().Elem(), len(paramsList), len(paramsList))
itemType = reflectElemArray.Index(0).Type()
itemTypeKind = itemType.Kind()
pointerRvElem = pointerRv.Elem()
pointerRvLength = pointerRvElem.Len()
)
if itemTypeKind == reflect.Ptr {
// Pointer element.
for i := 0; i < len(paramsList); i++ {
var tempReflectValue reflect.Value
if i < pointerRvLength {
// Might be nil.
tempReflectValue = pointerRvElem.Index(i).Elem()
}
if !tempReflectValue.IsValid() {
tempReflectValue = reflect.New(itemType.Elem()).Elem()
}
if err = c.Struct(paramsList[i], tempReflectValue, paramKeyToAttrMap, priorityTag); err != nil {
return err
}
reflectElemArray.Index(i).Set(tempReflectValue.Addr())
}
} else {
// Struct element.
for i := 0; i < len(paramsList); i++ {
var tempReflectValue reflect.Value
if i < pointerRvLength {
tempReflectValue = pointerRvElem.Index(i)
} else {
tempReflectValue = reflect.New(itemType).Elem()
}
if err = c.Struct(paramsList[i], tempReflectValue, paramKeyToAttrMap, priorityTag); err != nil {
return err
}
reflectElemArray.Index(i).Set(tempReflectValue)
}
}
pointerRv.Elem().Set(reflectElemArray)
return nil
}

View File

@ -0,0 +1,111 @@
// 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 (
"time"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
// Time converts `any` to time.Time.
func (c *Converter) Time(any interface{}, format ...string) (time.Time, error) {
// It's already this type.
if len(format) == 0 {
if v, ok := any.(time.Time); ok {
return v, nil
}
}
t, err := c.GTime(any, format...)
if err != nil {
return time.Time{}, err
}
if t != nil {
return t.Time, nil
}
return time.Time{}, nil
}
// Duration converts `any` to time.Duration.
// If `any` is string, then it uses time.ParseDuration to convert it.
// If `any` is numeric, then it converts `any` as nanoseconds.
func (c *Converter) Duration(any interface{}) (time.Duration, error) {
// It's already this type.
if v, ok := any.(time.Duration); ok {
return v, nil
}
s, err := c.String(any)
if err != nil {
return 0, err
}
if !utils.IsNumeric(s) {
return gtime.ParseDuration(s)
}
i, err := c.Int64(any)
if err != nil {
return 0, err
}
return time.Duration(i), nil
}
// GTime converts `any` to *gtime.Time.
// The parameter `format` can be used to specify the format of `any`.
// It returns the converted value that matched the first format of the formats slice.
// If no `format` given, it converts `any` using gtime.NewFromTimeStamp if `any` is numeric,
// or using gtime.StrToTime if `any` is string.
func (c *Converter) GTime(any interface{}, format ...string) (*gtime.Time, error) {
if empty.IsNil(any) {
return nil, nil
}
if v, ok := any.(localinterface.IGTime); ok {
return v.GTime(format...), nil
}
// It's already this type.
if len(format) == 0 {
if v, ok := any.(*gtime.Time); ok {
return v, nil
}
if t, ok := any.(time.Time); ok {
return gtime.New(t), nil
}
if t, ok := any.(*time.Time); ok {
return gtime.New(t), nil
}
}
s, err := c.String(any)
if err != nil {
return nil, err
}
if len(s) == 0 {
return gtime.New(), nil
}
// Priority conversion using given format.
if len(format) > 0 {
for _, item := range format {
t, err := gtime.StrToTimeFormat(s, item)
if err != nil {
return nil, err
}
if t != nil {
return t, nil
}
}
return nil, nil
}
if utils.IsNumeric(s) {
i, err := c.Int64(s)
if err != nil {
return nil, err
}
return gtime.NewFromTimeStamp(i), nil
} else {
return gtime.StrToTime(s)
}
}

View File

@ -0,0 +1,156 @@
// 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 (
"math"
"reflect"
"strconv"
"github.com/gogf/gf/v2/encoding/gbinary"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
func (c *Converter) Uint(any any) (uint, error) {
if empty.IsNil(any) {
return 0, nil
}
if v, ok := any.(uint); ok {
return v, nil
}
v, err := c.Uint64(any)
return uint(v), err
}
func (c *Converter) Uint8(any any) (uint8, error) {
if empty.IsNil(any) {
return 0, nil
}
if v, ok := any.(uint8); ok {
return v, nil
}
v, err := c.Uint64(any)
return uint8(v), err
}
func (c *Converter) Uint16(any any) (uint16, error) {
if empty.IsNil(any) {
return 0, nil
}
if v, ok := any.(uint16); ok {
return v, nil
}
v, err := c.Uint64(any)
return uint16(v), err
}
func (c *Converter) Uint32(any any) (uint32, error) {
if empty.IsNil(any) {
return 0, nil
}
if v, ok := any.(uint32); ok {
return v, nil
}
v, err := c.Uint64(any)
return uint32(v), err
}
func (c *Converter) Uint64(any any) (uint64, error) {
if empty.IsNil(any) {
return 0, nil
}
if v, ok := any.(uint64); ok {
return v, nil
}
rv := reflect.ValueOf(any)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
val := rv.Int()
if val < 0 {
return uint64(val), gerror.NewCodef(
gcode.CodeInvalidParameter,
`cannot convert negative value "%d" to uint64`,
val,
)
}
return uint64(val), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return rv.Uint(), nil
case reflect.Uintptr:
return rv.Uint(), nil
case reflect.Float32, reflect.Float64:
val := rv.Float()
if val < 0 {
return uint64(val), gerror.NewCodef(
gcode.CodeInvalidParameter,
`cannot convert negative value "%f" to uint64`,
val,
)
}
return uint64(val), nil
case reflect.Bool:
if rv.Bool() {
return 1, nil
}
return 0, nil
case reflect.Ptr:
if rv.IsNil() {
return 0, nil
}
if f, ok := any.(localinterface.IUint64); ok {
return f.Uint64(), nil
}
return c.Uint64(rv.Elem().Interface())
case reflect.Slice:
if rv.Type().Elem().Kind() == reflect.Uint8 {
return gbinary.DecodeToUint64(rv.Bytes()), nil
}
return 0, gerror.NewCodef(
gcode.CodeInvalidParameter,
`unsupport slice type "%s" for converting to uint64`,
rv.Type().String(),
)
case reflect.String:
var s = rv.String()
// Hexadecimal
if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
v, err := strconv.ParseUint(s[2:], 16, 64)
if err == nil {
return v, nil
}
return 0, gerror.WrapCodef(
gcode.CodeInvalidParameter,
err,
`cannot convert hexadecimal string "%s" to uint64`,
s,
)
}
// Decimal
if v, err := strconv.ParseUint(s, 10, 64); err == nil {
return v, nil
}
// Float64
if v, err := c.Float64(any); err == nil {
if math.IsNaN(v) {
return 0, nil
}
return uint64(v), nil
}
default:
if f, ok := any.(localinterface.IUint64); ok {
return f.Uint64(), nil
}
}
return 0, gerror.NewCodef(
gcode.CodeInvalidParameter,
`unsupport value type "%s" for converting to uint64`,
reflect.TypeOf(any).String(),
)
}

View File

@ -6,144 +6,14 @@
package gconv
import (
"reflect"
"strconv"
"github.com/gogf/gf/v2/encoding/gbinary"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
// Float32 converts `any` to float32.
func Float32(any any) float32 {
v, _ := doFloat32(any)
v, _ := defaultConverter.Float32(any)
return v
}
func doFloat32(any any) (float32, error) {
if empty.IsNil(any) {
return 0, nil
}
switch value := any.(type) {
case float32:
return value, nil
case float64:
return float32(value), nil
case []byte:
// TODO: It might panic here for these types.
return gbinary.DecodeToFloat32(value), nil
default:
rv := reflect.ValueOf(any)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float32(rv.Int()), nil
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return float32(rv.Uint()), nil
case reflect.Float32, reflect.Float64:
return float32(rv.Float()), nil
case reflect.Bool:
if rv.Bool() {
return 1, nil
}
return 0, nil
case reflect.String:
f, err := strconv.ParseFloat(rv.String(), 32)
if err != nil {
return 0, gerror.WrapCodef(
gcode.CodeInvalidParameter, err, "converting string to float32 failed for: %v", any,
)
}
return float32(f), nil
case reflect.Ptr:
if rv.IsNil() {
return 0, nil
}
if f, ok := value.(localinterface.IFloat32); ok {
return f.Float32(), nil
}
return doFloat32(rv.Elem().Interface())
default:
if f, ok := value.(localinterface.IFloat32); ok {
return f.Float32(), nil
}
v, err := strconv.ParseFloat(String(any), 32)
if err != nil {
return 0, gerror.WrapCodef(
gcode.CodeInvalidParameter, err, "converting string to float32 failed for: %v", any,
)
}
return float32(v), nil
}
}
}
// Float64 converts `any` to float64.
func Float64(any any) float64 {
v, _ := doFloat64(any)
v, _ := defaultConverter.Float64(any)
return v
}
func doFloat64(any any) (float64, error) {
if empty.IsNil(any) {
return 0, nil
}
switch value := any.(type) {
case float32:
return float64(value), nil
case float64:
return value, nil
case []byte:
// TODO: It might panic here for these types.
return gbinary.DecodeToFloat64(value), nil
default:
rv := reflect.ValueOf(any)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float64(rv.Int()), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return float64(rv.Uint()), nil
case reflect.Uintptr:
return float64(rv.Uint()), nil
case reflect.Float32, reflect.Float64:
// Please Note:
// When the type is float32 or a custom type defined based on float32,
// switching to float64 may result in a few extra decimal places.
return rv.Float(), nil
case reflect.Bool:
if rv.Bool() {
return 1, nil
}
return 0, nil
case reflect.String:
f, err := strconv.ParseFloat(rv.String(), 64)
if err != nil {
return 0, gerror.WrapCodef(
gcode.CodeInvalidParameter, err, "converting string to float64 failed for: %v", any,
)
}
return f, nil
case reflect.Ptr:
if rv.IsNil() {
return 0, nil
}
if f, ok := value.(localinterface.IFloat64); ok {
return f.Float64(), nil
}
return doFloat64(rv.Elem().Interface())
default:
if f, ok := value.(localinterface.IFloat64); ok {
return f.Float64(), nil
}
v, err := strconv.ParseFloat(String(any), 64)
if err != nil {
return 0, gerror.WrapCodef(
gcode.CodeInvalidParameter, err, "converting string to float64 failed for: %v", any,
)
}
return v, nil
}
}
}

View File

@ -6,177 +6,32 @@
package gconv
import (
"math"
"reflect"
"strconv"
"github.com/gogf/gf/v2/encoding/gbinary"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
// Int converts `any` to int.
func Int(any any) int {
v, _ := doInt(any)
v, _ := defaultConverter.Int(any)
return v
}
func doInt(any any) (int, error) {
if v, ok := any.(int); ok {
return v, nil
}
v, err := doInt64(any)
if err != nil {
return 0, err
}
return int(v), nil
}
// Int8 converts `any` to int8.
func Int8(any any) int8 {
v, _ := doInt8(any)
v, _ := defaultConverter.Int8(any)
return v
}
func doInt8(any any) (int8, error) {
if v, ok := any.(int8); ok {
return v, nil
}
v, err := doInt64(any)
if err != nil {
return 0, err
}
return int8(v), nil
}
// Int16 converts `any` to int16.
func Int16(any any) int16 {
v, _ := doInt16(any)
v, _ := defaultConverter.Int16(any)
return v
}
func doInt16(any any) (int16, error) {
if v, ok := any.(int16); ok {
return v, nil
}
v, err := doInt64(any)
if err != nil {
return 0, err
}
return int16(v), nil
}
// Int32 converts `any` to int32.
func Int32(any any) int32 {
v, _ := doInt32(any)
v, _ := defaultConverter.Int32(any)
return v
}
func doInt32(any any) (int32, error) {
if v, ok := any.(int32); ok {
return v, nil
}
v, err := doInt64(any)
if err != nil {
return 0, err
}
return int32(v), nil
}
// Int64 converts `any` to int64.
func Int64(any any) int64 {
v, _ := doInt64(any)
v, _ := defaultConverter.Int64(any)
return v
}
func doInt64(any any) (int64, error) {
if empty.IsNil(any) {
return 0, nil
}
if v, ok := any.(int64); ok {
return v, nil
}
rv := reflect.ValueOf(any)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return rv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return int64(rv.Uint()), nil
case reflect.Uintptr:
return int64(rv.Uint()), nil
case reflect.Float32, reflect.Float64:
return int64(rv.Float()), nil
case reflect.Bool:
if rv.Bool() {
return 1, nil
}
return 0, nil
case reflect.Ptr:
if rv.IsNil() {
return 0, nil
}
if f, ok := any.(localinterface.IInt64); ok {
return f.Int64(), nil
}
return doInt64(rv.Elem().Interface())
case reflect.Slice:
// TODO: It might panic here for these types.
if rv.Type().Elem().Kind() == reflect.Uint8 {
return gbinary.DecodeToInt64(rv.Bytes()), nil
}
case reflect.String:
var (
s = rv.String()
isMinus = false
)
if len(s) > 0 {
if s[0] == '-' {
isMinus = true
s = s[1:]
} else if s[0] == '+' {
s = s[1:]
}
}
// Hexadecimal.
if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
if v, e := strconv.ParseInt(s[2:], 16, 64); e == nil {
if isMinus {
return -v, nil
}
return v, nil
}
}
// Decimal.
if v, e := strconv.ParseInt(s, 10, 64); e == nil {
if isMinus {
return -v, nil
}
return v, nil
}
// Float64.
valueInt64, err := doFloat64(s)
if err != nil {
return 0, err
}
if math.IsNaN(valueInt64) {
return 0, nil
} else {
if isMinus {
return -int64(valueInt64), nil
}
return int64(valueInt64), nil
}
default:
if f, ok := any.(localinterface.IInt64); ok {
return f.Int64(), nil
}
}
return 0, gerror.NewCodef(
gcode.CodeInvalidParameter,
`unsupport value type for converting to int64: %v`,
reflect.TypeOf(any),
)
}

View File

@ -6,17 +6,6 @@
package gconv
import (
"reflect"
"strings"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
"github.com/gogf/gf/v2/util/gtag"
)
type recursiveType string
const (
@ -28,7 +17,7 @@ const (
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.
// a map[string]any type variable.
Deep bool
// OmitEmpty ignores the attributes that has json `omitempty` tag.
@ -38,516 +27,30 @@ type MapOption struct {
Tags []string
}
// Map converts any variable `value` to map[string]interface{}. If the parameter `value` is not a
// Map converts any variable `value` to map[string]any. 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 `priorityTagAndFieldName` specifies the most priority
// priorityTagAndFieldName that will be detected, otherwise it detects the priorityTagAndFieldName in order of:
// gconv, json, field name.
func Map(value interface{}, option ...MapOption) map[string]interface{} {
return doMapConvert(value, recursiveTypeAuto, false, option...)
func Map(value any, option ...MapOption) map[string]any {
return defaultConverter.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.
// a map[string]any type variable.
// Deprecated: used Map instead.
func MapDeep(value interface{}, tags ...string) map[string]interface{} {
return doMapConvert(value, recursiveTypeTrue, false, MapOption{
func MapDeep(value any, tags ...string) map[string]any {
return defaultConverter.doMapConvert(value, recursiveTypeTrue, false, MapOption{
Deep: true,
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, option ...MapOption) map[string]interface{} {
if value == nil {
return nil
}
// It redirects to its underlying value if it has implemented interface iVal.
if v, ok := value.(localinterface.IVal); ok {
value = v.Val()
}
var (
usedOption = getUsedMapOption(option...)
newTags = gtag.StructTagPriority
)
if usedOption.Deep {
recursive = recursiveTypeTrue
}
switch len(usedOption.Tags) {
case 0:
// No need handling.
case 1:
newTags = append(strings.Split(usedOption.Tags[0], ","), gtag.StructTagPriority...)
default:
newTags = append(usedOption.Tags, gtag.StructTagPriority...)
}
// Assert the common combination of types, and finally it uses reflection.
dataMap := make(map[string]interface{})
switch r := value.(type) {
case string:
// If it is a JSON string, automatically unmarshal it!
if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' {
if err := json.UnmarshalUseNumber([]byte(r), &dataMap); err != nil {
return nil
}
} else {
return nil
}
case []byte:
// If it is a JSON string, automatically unmarshal it!
if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' {
if err := json.UnmarshalUseNumber(r, &dataMap); err != nil {
return nil
}
} else {
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,
Option: recursiveOption,
},
)
}
case map[interface{}]string:
for k, v := range r {
dataMap[String(k)] = v
}
case map[interface{}]int:
for k, v := range r {
dataMap[String(k)] = v
}
case map[interface{}]uint:
for k, v := range r {
dataMap[String(k)] = v
}
case map[interface{}]float32:
for k, v := range r {
dataMap[String(k)] = v
}
case map[interface{}]float64:
for k, v := range r {
dataMap[String(k)] = v
}
case map[string]bool:
for k, v := range r {
dataMap[k] = v
}
case map[string]int:
for k, v := range r {
dataMap[k] = v
}
case map[string]uint:
for k, v := range r {
dataMap[k] = v
}
case map[string]float32:
for k, v := range r {
dataMap[k] = v
}
case map[string]float64:
for k, v := range r {
dataMap[k] = v
}
case map[string]string:
for k, v := range r {
dataMap[k] = v
}
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(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: v,
RecursiveType: recursive,
RecursiveOption: recursive == recursiveTypeTrue,
Option: recursiveOption,
},
)
}
} else {
// It returns the map directly without any changing.
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,
Option: recursiveOption,
},
)
}
case map[int]string:
for k, v := range r {
dataMap[String(k)] = v
}
case map[uint]string:
for k, v := range r {
dataMap[String(k)] = v
}
default:
// Not a common type, it then uses reflection for conversion.
var reflectValue reflect.Value
if v, ok := value.(reflect.Value); ok {
reflectValue = v
} else {
reflectValue = reflect.ValueOf(value)
}
reflectKind := reflectValue.Kind()
// If it is a pointer, we should find its real data type.
for reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
}
switch reflectKind {
// If `value` is type of array, it converts the value of even number index as its key and
// the value of odd number index as its corresponding value, for example:
// []string{"k1","v1","k2","v2"} => map[string]interface{}{"k1":"v1", "k2":"v2"}
// []string{"k1","v1","k2"} => map[string]interface{}{"k1":"v1", "k2":nil}
case reflect.Slice, reflect.Array:
length := reflectValue.Len()
for i := 0; i < length; i += 2 {
if i+1 < length {
dataMap[String(reflectValue.Index(i).Interface())] = reflectValue.Index(i + 1).Interface()
} else {
dataMap[String(reflectValue.Index(i).Interface())] = nil
}
}
case reflect.Map, reflect.Struct, reflect.Interface:
recursiveOption := usedOption
recursiveOption.Tags = newTags
convertedValue := doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: true,
Value: value,
RecursiveType: recursive,
RecursiveOption: recursive == recursiveTypeTrue,
Option: recursiveOption,
MustMapReturn: mustMapReturn,
},
)
if m, ok := convertedValue.(map[string]interface{}); ok {
return m
}
return nil
default:
return nil
}
}
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.
Option MapOption // Map converting option.
MustMapReturn bool // Must return map instead of Value when empty.
}
func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) interface{} {
if !in.IsRoot && !in.RecursiveOption {
return in.Value
}
var reflectValue reflect.Value
if v, ok := in.Value.(reflect.Value); ok {
reflectValue = v
in.Value = v.Interface()
} else {
reflectValue = reflect.ValueOf(in.Value)
}
reflectKind := reflectValue.Kind()
// If it is a pointer, we should find its real data type.
for reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
}
switch reflectKind {
case reflect.Map:
var (
mapIter = reflectValue.MapRange()
dataMap = make(map[string]interface{})
)
for mapIter.Next() {
var (
mapKeyValue = mapIter.Value()
mapValue interface{}
)
switch {
case mapKeyValue.IsZero():
if utils.CanCallIsNil(mapKeyValue) && mapKeyValue.IsNil() {
// quick check for nil value.
mapValue = nil
} else {
// in case of:
// exception recovered: reflect: call of reflect.Value.Interface on zero Value
mapValue = reflect.New(mapKeyValue.Type()).Elem().Interface()
}
default:
mapValue = mapKeyValue.Interface()
}
dataMap[String(mapIter.Key().Interface())] = doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: mapValue,
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Option: in.Option,
},
)
}
return dataMap
case reflect.Struct:
var dataMap = make(map[string]interface{})
// Map converting interface check.
if v, ok := in.Value.(localinterface.IMapStrAny); ok {
// Value copy, in case of concurrent safety.
for mapK, mapV := range v.MapStrAny() {
if in.RecursiveOption {
dataMap[mapK] = doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: mapV,
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Option: in.Option,
},
)
} else {
dataMap[mapK] = mapV
}
}
if len(dataMap) > 0 {
return dataMap
}
}
// Using reflect for converting.
var (
rtField reflect.StructField
rvField reflect.Value
reflectType = reflectValue.Type() // attribute value type.
mapKey = "" // mapKey may be the tag name or the struct attribute name.
)
for i := 0; i < reflectValue.NumField(); i++ {
rtField = reflectType.Field(i)
rvField = reflectValue.Field(i)
// Only convert the public attributes.
fieldName := rtField.Name
if !utils.IsLetterUpper(fieldName[0]) {
continue
}
mapKey = ""
fieldTag := rtField.Tag
for _, tag := range in.Option.Tags {
if mapKey = fieldTag.Get(tag); mapKey != "" {
break
}
}
if mapKey == "" {
mapKey = fieldName
} else {
// Support json tag feature: -, omitempty
mapKey = strings.TrimSpace(mapKey)
if mapKey == "-" {
continue
}
array := strings.Split(mapKey, ",")
if len(array) > 1 {
switch strings.TrimSpace(array[1]) {
case "omitempty":
if in.Option.OmitEmpty && empty.IsEmpty(rvField.Interface()) {
continue
} else {
mapKey = strings.TrimSpace(array[0])
}
default:
mapKey = strings.TrimSpace(array[0])
}
}
if mapKey == "" {
mapKey = fieldName
}
}
if in.RecursiveOption || rtField.Anonymous {
// Do map converting recursively.
var (
rvAttrField = rvField
rvAttrKind = rvField.Kind()
)
if rvAttrKind == reflect.Ptr {
rvAttrField = rvField.Elem()
rvAttrKind = rvAttrField.Kind()
}
switch rvAttrKind {
case reflect.Struct:
// Embedded struct and has no fields, just ignores it.
// Eg: gmeta.Meta
if rvAttrField.Type().NumField() == 0 {
continue
}
var (
hasNoTag = mapKey == fieldName
// DO NOT use rvAttrField.Interface() here,
// as it might be changed from pointer to struct.
rvInterface = rvField.Interface()
)
switch {
case hasNoTag && rtField.Anonymous:
// It means this attribute field has no tag.
// Overwrite the attribute with sub-struct attribute fields.
anonymousValue := doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: rvInterface,
RecursiveType: in.RecursiveType,
RecursiveOption: true,
Option: in.Option,
})
if m, ok := anonymousValue.(map[string]interface{}); ok {
for k, v := range m {
dataMap[k] = v
}
} else {
dataMap[mapKey] = rvInterface
}
// It means this attribute field has desired tag.
case !hasNoTag && rtField.Anonymous:
dataMap[mapKey] = doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: rvInterface,
RecursiveType: in.RecursiveType,
RecursiveOption: true,
Option: in.Option,
})
default:
dataMap[mapKey] = doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: rvInterface,
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Option: in.Option,
})
}
// The struct attribute is type of slice.
case reflect.Array, reflect.Slice:
length := rvAttrField.Len()
if length == 0 {
dataMap[mapKey] = rvAttrField.Interface()
break
}
array := make([]interface{}, length)
for arrayIndex := 0; arrayIndex < length; arrayIndex++ {
array[arrayIndex] = doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: rvAttrField.Index(arrayIndex).Interface(),
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Option: in.Option,
},
)
}
dataMap[mapKey] = array
case reflect.Map:
var (
mapIter = rvAttrField.MapRange()
nestedMap = make(map[string]interface{})
)
for mapIter.Next() {
nestedMap[String(mapIter.Key().Interface())] = doMapConvertForMapOrStructValue(
doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: mapIter.Value().Interface(),
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Option: in.Option,
},
)
}
dataMap[mapKey] = nestedMap
default:
if rvField.IsValid() {
dataMap[mapKey] = reflectValue.Field(i).Interface()
} else {
dataMap[mapKey] = nil
}
}
} else {
// No recursive map value converting
if rvField.IsValid() {
dataMap[mapKey] = reflectValue.Field(i).Interface()
} else {
dataMap[mapKey] = nil
}
}
}
if !in.MustMapReturn && len(dataMap) == 0 {
return in.Value
}
return dataMap
// The given value is type of slice.
case reflect.Array, reflect.Slice:
length := reflectValue.Len()
if length == 0 {
break
}
array := make([]interface{}, reflectValue.Len())
for i := 0; i < length; i++ {
array[i] = doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
IsRoot: false,
Value: reflectValue.Index(i).Interface(),
RecursiveType: in.RecursiveType,
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
Option: in.Option,
})
}
return array
default:
}
return in.Value
}
// MapStrStr converts `value` to map[string]string.
// Note that there might be data copy for this map type converting.
func MapStrStr(value interface{}, option ...MapOption) map[string]string {
func MapStrStr(value any, option ...MapOption) map[string]string {
if r, ok := value.(map[string]string); ok {
return r
}
@ -565,7 +68,7 @@ func MapStrStr(value interface{}, option ...MapOption) 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 {
func MapStrStrDeep(value any, tags ...string) map[string]string {
if r, ok := value.(map[string]string); ok {
return r
}

View File

@ -6,130 +6,9 @@
package gconv
import (
"reflect"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
)
// MapToMap converts any map type variable `params` to another map type variable `pointer`
// using reflect.
// See doMapToMap.
func MapToMap(params any, pointer any, mapping ...map[string]string) error {
return Scan(params, pointer, mapping...)
}
// doMapToMap converts any map type variable `params` to another map type variable `pointer`.
//
// The parameter `params` can be any type of map, like:
// map[string]string, map[string]struct, map[string]*struct, reflect.Value, etc.
//
// The parameter `pointer` should be type of *map, like:
// map[int]string, map[string]struct, map[string]*struct, reflect.Value, etc.
//
// 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(cf *ConvertConfig, params any, pointer any, mapping ...map[string]string) (err error) {
var (
paramsRv reflect.Value
paramsKind reflect.Kind
keyToAttributeNameMapping map[string]string
)
if len(mapping) > 0 {
keyToAttributeNameMapping = mapping[0]
}
if v, ok := params.(reflect.Value); ok {
paramsRv = v
} else {
paramsRv = reflect.ValueOf(params)
}
paramsKind = paramsRv.Kind()
if paramsKind == reflect.Ptr {
paramsRv = paramsRv.Elem()
paramsKind = paramsRv.Kind()
}
if paramsKind != reflect.Map {
return doMapToMap(cf, Map(params), pointer, mapping...)
}
// Empty params map, no need continue.
if paramsRv.Len() == 0 {
return nil
}
var pointerRv reflect.Value
if v, ok := pointer.(reflect.Value); ok {
pointerRv = v
} else {
pointerRv = reflect.ValueOf(pointer)
}
pointerKind := pointerRv.Kind()
for pointerKind == reflect.Ptr {
pointerRv = pointerRv.Elem()
pointerKind = pointerRv.Kind()
}
if pointerKind != reflect.Map {
return gerror.NewCodef(
gcode.CodeInvalidParameter,
`destination pointer should be type of *map, but got: %s`,
pointerKind,
)
}
defer func() {
// Catch the panic, especially the reflection operation panics.
if exception := recover(); exception != nil {
if v, ok := exception.(error); ok && gerror.HasStack(v) {
err = v
} else {
err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception)
}
}
}()
var (
paramsKeys = paramsRv.MapKeys()
pointerKeyType = pointerRv.Type().Key()
pointerValueType = pointerRv.Type().Elem()
pointerValueKind = pointerValueType.Kind()
dataMap = reflect.MakeMapWithSize(pointerRv.Type(), len(paramsKeys))
)
// Retrieve the true element type of target map.
if pointerValueKind == reflect.Ptr {
pointerValueKind = pointerValueType.Elem().Kind()
}
for _, key := range paramsKeys {
mapValue := reflect.New(pointerValueType).Elem()
switch pointerValueKind {
case reflect.Map, reflect.Struct:
if err = doStruct(
paramsRv.MapIndex(key).Interface(), mapValue, keyToAttributeNameMapping, "",
); err != nil {
return err
}
default:
mapValue.Set(
reflect.ValueOf(
doConvert(
cf, doConvertInput{
FromValue: paramsRv.MapIndex(key).Interface(),
ToTypeName: pointerValueType.String(),
ReferValue: mapValue,
Extra: nil,
}),
),
)
}
var mapKey = reflect.ValueOf(
doConvert(
cf,
doConvertInput{
FromValue: key.Interface(),
ToTypeName: pointerKeyType.Name(),
ReferValue: reflect.New(pointerKeyType).Elem().Interface(),
Extra: nil,
},
),
)
dataMap.SetMapIndex(mapKey, mapValue)
}
pointerRv.Set(dataMap)
return nil
}

View File

@ -6,121 +6,8 @@
package gconv
import (
"reflect"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
)
// MapToMaps converts any slice type variable `params` to another map slice type variable `pointer`.
// See doMapToMaps.
func MapToMaps(params interface{}, pointer interface{}, mapping ...map[string]string) error {
func MapToMaps(params any, pointer any, mapping ...map[string]string) error {
return Scan(params, pointer, mapping...)
}
// doMapToMaps converts any map type variable `params` to another map slice variable `pointer`.
//
// The parameter `params` can be type of []map, []*map, []struct, []*struct.
//
// The parameter `pointer` should be type of []map, []*map.
//
// The optional parameter `mapping` is used for struct attribute to map key mapping, which makes
// sense only if the item of `params` is type struct.
func doMapToMaps(params interface{}, pointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) {
// Params and its element type check.
var (
paramsRv reflect.Value
paramsKind reflect.Kind
)
if v, ok := params.(reflect.Value); ok {
paramsRv = v
} else {
paramsRv = reflect.ValueOf(params)
}
paramsKind = paramsRv.Kind()
if paramsKind == reflect.Ptr {
paramsRv = paramsRv.Elem()
paramsKind = paramsRv.Kind()
}
if paramsKind != reflect.Array && paramsKind != reflect.Slice {
return gerror.NewCode(
gcode.CodeInvalidParameter,
"params should be type of slice, example: []map/[]*map/[]struct/[]*struct",
)
}
var (
paramsElem = paramsRv.Type().Elem()
paramsElemKind = paramsElem.Kind()
)
if paramsElemKind == reflect.Ptr {
paramsElem = paramsElem.Elem()
paramsElemKind = paramsElem.Kind()
}
if paramsElemKind != reflect.Map &&
paramsElemKind != reflect.Struct &&
paramsElemKind != reflect.Interface {
return gerror.NewCodef(
gcode.CodeInvalidParameter,
"params element should be type of map/*map/struct/*struct, but got: %s",
paramsElemKind,
)
}
// Empty slice, no need continue.
if paramsRv.Len() == 0 {
return nil
}
// Pointer and its element type check.
var (
pointerRv = reflect.ValueOf(pointer)
pointerKind = pointerRv.Kind()
)
for pointerKind == reflect.Ptr {
pointerRv = pointerRv.Elem()
pointerKind = pointerRv.Kind()
}
if pointerKind != reflect.Array && pointerKind != reflect.Slice {
return gerror.NewCode(gcode.CodeInvalidParameter, "pointer should be type of *[]map/*[]*map")
}
var (
pointerElemType = pointerRv.Type().Elem()
pointerElemKind = pointerElemType.Kind()
)
if pointerElemKind == reflect.Ptr {
pointerElemKind = pointerElemType.Elem().Kind()
}
if pointerElemKind != reflect.Map {
return gerror.NewCode(gcode.CodeInvalidParameter, "pointer element should be type of map/*map")
}
defer func() {
// Catch the panic, especially the reflection operation panics.
if exception := recover(); exception != nil {
if v, ok := exception.(error); ok && gerror.HasStack(v) {
err = v
} else {
err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception)
}
}
}()
var (
pointerSlice = reflect.MakeSlice(pointerRv.Type(), paramsRv.Len(), paramsRv.Len())
)
for i := 0; i < paramsRv.Len(); i++ {
var item reflect.Value
if pointerElemType.Kind() == reflect.Ptr {
item = reflect.New(pointerElemType.Elem())
if err = MapToMap(paramsRv.Index(i).Interface(), item, paramKeyToAttrMap...); err != nil {
return err
}
pointerSlice.Index(i).Set(item)
} else {
item = reflect.New(pointerElemType)
if err = MapToMap(paramsRv.Index(i).Interface(), item, paramKeyToAttrMap...); err != nil {
return err
}
pointerSlice.Index(i).Set(item.Elem())
}
}
pointerRv.Set(pointerSlice)
return
}

View File

@ -6,15 +6,6 @@
package gconv
import (
"reflect"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
// Scan automatically checks the type of `pointer` and converts `params` to `pointer`.
// It supports various types of parameter conversions, including:
// 1. Basic types (int, string, float, etc.)
@ -26,326 +17,5 @@ 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
}
// Check if dstPointer is nil, which is an invalid parameter
if dstPointer == nil {
return gerror.NewCode(
gcode.CodeInvalidParameter,
`destination pointer should not be nil`,
)
}
// Get the reflection type and value of dstPointer
var (
dstPointerReflectType reflect.Type
dstPointerReflectValue reflect.Value
)
if v, ok := dstPointer.(reflect.Value); ok {
dstPointerReflectValue = v
dstPointerReflectType = v.Type()
} else {
dstPointerReflectValue = reflect.ValueOf(dstPointer)
// Do not use dstPointerReflectValue.Type() as dstPointerReflectValue might be zero
dstPointerReflectType = reflect.TypeOf(dstPointer)
}
// Validate the kind of dstPointer
var dstPointerReflectKind = dstPointerReflectType.Kind()
if dstPointerReflectKind != reflect.Ptr {
// If dstPointer is not a pointer, try to get its address
if dstPointerReflectValue.CanAddr() {
dstPointerReflectValue = dstPointerReflectValue.Addr()
dstPointerReflectType = dstPointerReflectValue.Type()
dstPointerReflectKind = dstPointerReflectType.Kind()
} else {
// If dstPointer is not a pointer and cannot be addressed, return an error
return gerror.NewCodef(
gcode.CodeInvalidParameter,
`destination pointer should be type of pointer, but got type: %v`,
dstPointerReflectType,
)
}
}
// Get the reflection value of srcValue
var srcValueReflectValue reflect.Value
if v, ok := srcValue.(reflect.Value); ok {
srcValueReflectValue = v
} else {
srcValueReflectValue = reflect.ValueOf(srcValue)
}
// Get the element type and kind of dstPointer
var (
dstPointerReflectValueElem = dstPointerReflectValue.Elem()
dstPointerReflectValueElemKind = dstPointerReflectValueElem.Kind()
)
// Handle multiple level pointers
if dstPointerReflectValueElemKind == reflect.Ptr {
if dstPointerReflectValueElem.IsNil() {
// Create a new value for the pointer dereference
nextLevelPtr := reflect.New(dstPointerReflectValueElem.Type().Elem())
// Recursively scan into the dereferenced pointer
if err = Scan(srcValueReflectValue, nextLevelPtr, paramKeyToAttrMap...); err == nil {
dstPointerReflectValueElem.Set(nextLevelPtr)
}
return
}
return Scan(srcValueReflectValue, dstPointerReflectValueElem, paramKeyToAttrMap...)
}
// Check if srcValue and dstPointer are the same type, in which case direct assignment can be performed
if ok := doConvertWithTypeCheck(srcValueReflectValue, dstPointerReflectValueElem); ok {
return nil
}
// Handle different destination types
switch dstPointerReflectValueElemKind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
// Convert to int type
dstPointerReflectValueElem.SetInt(Int64(srcValue))
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
// Convert to uint type
dstPointerReflectValueElem.SetUint(Uint64(srcValue))
return nil
case reflect.Float32, reflect.Float64:
// Convert to float type
dstPointerReflectValueElem.SetFloat(Float64(srcValue))
return nil
case reflect.String:
// Convert to string type
dstPointerReflectValueElem.SetString(String(srcValue))
return nil
case reflect.Bool:
// Convert to bool type
dstPointerReflectValueElem.SetBool(Bool(srcValue))
return nil
case reflect.Slice:
// Handle slice type conversion
var (
dstElemType = dstPointerReflectValueElem.Type().Elem()
dstElemKind = dstElemType.Kind()
)
// The slice element might be a pointer type
if dstElemKind == reflect.Ptr {
dstElemType = dstElemType.Elem()
dstElemKind = dstElemType.Kind()
}
// Special handling for struct or map slice elements
if dstElemKind == reflect.Struct || dstElemKind == reflect.Map {
return doScanForComplicatedTypes(cf, srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...)
}
// Handle basic type slice conversions
var srcValueReflectValueKind = srcValueReflectValue.Kind()
if srcValueReflectValueKind == reflect.Slice || srcValueReflectValueKind == reflect.Array {
var (
srcLen = srcValueReflectValue.Len()
newSlice = reflect.MakeSlice(dstPointerReflectValueElem.Type(), srcLen, srcLen)
)
for i := 0; i < srcLen; i++ {
srcElem := srcValueReflectValue.Index(i).Interface()
switch dstElemType.Kind() {
case reflect.String:
newSlice.Index(i).SetString(String(srcElem))
case reflect.Int:
newSlice.Index(i).SetInt(Int64(srcElem))
case reflect.Int64:
newSlice.Index(i).SetInt(Int64(srcElem))
case reflect.Float64:
newSlice.Index(i).SetFloat(Float64(srcElem))
case reflect.Bool:
newSlice.Index(i).SetBool(Bool(srcElem))
default:
return Scan(
srcElem, newSlice.Index(i).Addr().Interface(), paramKeyToAttrMap...,
)
}
}
dstPointerReflectValueElem.Set(newSlice)
return nil
}
return doScanForComplicatedTypes(cf, srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...)
default:
// Handle complex types (structs, maps, etc.)
return doScanForComplicatedTypes(cf, srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...)
}
}
// doScanForComplicatedTypes handles the scanning of complex data types.
// It supports converting between maps, structs, and slices of these types.
// The function first attempts JSON conversion, then falls back to specific type handling.
//
// It supports `pointer` in type of `*map/*[]map/*[]*map/*struct/**struct/*[]struct/*[]*struct` for converting.
//
// Parameters:
// - srcValue: The source value to convert from
// - dstPointer: The destination pointer to convert to
// - 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,
) error {
// Try JSON conversion first
ok, err := doConvertWithJsonCheck(srcValue, dstPointer)
if err != nil {
return err
}
if ok {
return nil
}
// Handle specific type conversions
var (
dstPointerReflectTypeElem = dstPointerReflectType.Elem()
dstPointerReflectTypeElemKind = dstPointerReflectTypeElem.Kind()
keyToAttributeNameMapping map[string]string
)
if len(paramKeyToAttrMap) > 0 {
keyToAttributeNameMapping = paramKeyToAttrMap[0]
}
// Handle different destination types
switch dstPointerReflectTypeElemKind {
case reflect.Map:
// Convert map to map
return doMapToMap(cf, srcValue, dstPointer, paramKeyToAttrMap...)
case reflect.Array, reflect.Slice:
var (
sliceElem = dstPointerReflectTypeElem.Elem()
sliceElemKind = sliceElem.Kind()
)
// Handle pointer elements
for sliceElemKind == reflect.Ptr {
sliceElem = sliceElem.Elem()
sliceElemKind = sliceElem.Kind()
}
if sliceElemKind == reflect.Map {
// Convert to slice of maps
return doMapToMaps(srcValue, dstPointer, paramKeyToAttrMap...)
}
// Convert to slice of structs
return doStructs(srcValue, dstPointer, keyToAttributeNameMapping, "")
default:
// Convert to single struct
return doStruct(srcValue, dstPointer, keyToAttributeNameMapping, "")
}
}
// doConvertWithTypeCheck supports `pointer` in type of `*map/*[]map/*[]*map/*struct/**struct/*[]struct/*[]*struct`
// for converting.
func doConvertWithTypeCheck(srcValueReflectValue, dstPointerReflectValueElem reflect.Value) (ok bool) {
if !dstPointerReflectValueElem.IsValid() || !srcValueReflectValue.IsValid() {
return false
}
switch {
// Examples:
// UploadFile => UploadFile
// []UploadFile => []UploadFile
// *UploadFile => *UploadFile
// *[]UploadFile => *[]UploadFile
// map[int][int] => map[int][int]
// []map[int][int] => []map[int][int]
// *[]map[int][int] => *[]map[int][int]
case dstPointerReflectValueElem.Type() == srcValueReflectValue.Type():
dstPointerReflectValueElem.Set(srcValueReflectValue)
return true
// Examples:
// UploadFile => *UploadFile
// []UploadFile => *[]UploadFile
// map[int][int] => *map[int][int]
// []map[int][int] => *[]map[int][int]
case dstPointerReflectValueElem.Kind() == reflect.Ptr &&
dstPointerReflectValueElem.Elem().IsValid() &&
dstPointerReflectValueElem.Elem().Type() == srcValueReflectValue.Type():
dstPointerReflectValueElem.Elem().Set(srcValueReflectValue)
return true
// Examples:
// *UploadFile => UploadFile
// *[]UploadFile => []UploadFile
// *map[int][int] => map[int][int]
// *[]map[int][int] => []map[int][int]
case srcValueReflectValue.Kind() == reflect.Ptr &&
srcValueReflectValue.Elem().IsValid() &&
dstPointerReflectValueElem.Type() == srcValueReflectValue.Elem().Type():
dstPointerReflectValueElem.Set(srcValueReflectValue.Elem())
return true
default:
return false
}
}
// doConvertWithJsonCheck attempts to convert the source value to the destination
// using JSON marshaling and unmarshaling. This is particularly useful for complex
// types that can be represented as JSON.
//
// Parameters:
// - srcValue: The source value to convert from
// - dstPointer: The destination pointer to convert to
//
// Returns:
// - bool: true if JSON conversion was successful
// - error: any error that occurred during conversion
func doConvertWithJsonCheck(srcValue any, dstPointer any) (ok bool, err error) {
switch valueResult := srcValue.(type) {
case []byte:
if json.Valid(valueResult) {
if dstPointerReflectType, ok := dstPointer.(reflect.Value); ok {
if dstPointerReflectType.Kind() == reflect.Ptr {
if dstPointerReflectType.IsNil() {
return false, nil
}
return true, json.UnmarshalUseNumber(valueResult, dstPointerReflectType.Interface())
} else if dstPointerReflectType.CanAddr() {
return true, json.UnmarshalUseNumber(valueResult, dstPointerReflectType.Addr().Interface())
}
} else {
return true, json.UnmarshalUseNumber(valueResult, dstPointer)
}
}
case string:
if valueBytes := []byte(valueResult); json.Valid(valueBytes) {
if dstPointerReflectType, ok := dstPointer.(reflect.Value); ok {
if dstPointerReflectType.Kind() == reflect.Ptr {
if dstPointerReflectType.IsNil() {
return false, nil
}
return true, json.UnmarshalUseNumber(valueBytes, dstPointerReflectType.Interface())
} else if dstPointerReflectType.CanAddr() {
return true, json.UnmarshalUseNumber(valueBytes, dstPointerReflectType.Addr().Interface())
}
} else {
return true, json.UnmarshalUseNumber(valueBytes, dstPointer)
}
}
default:
// The `params` might be struct that implements interface function Interface, eg: gvar.Var.
if v, ok := srcValue.(localinterface.IInterface); ok {
return doConvertWithJsonCheck(v.Interface(), dstPointer)
}
}
return false, nil
return defaultConverter.Scan(srcValue, dstPointer, paramKeyToAttrMap...)
}

View File

@ -6,19 +6,6 @@
package gconv
import (
"reflect"
"strings"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
"github.com/gogf/gf/v2/util/gconv/internal/structcache"
)
// Struct maps the params key-value pairs to the corresponding struct object's attributes.
// The third parameter `mapping` is unnecessary, indicating the mapping rules between the
// custom key name and the attribute name(case-sensitive).
@ -40,625 +27,5 @@ func Struct(params any, pointer any, paramKeyToAttrMap ...map[string]string) (er
// 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 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 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.
return nil
}
if pointer == nil {
return gerror.NewCode(gcode.CodeInvalidParameter, "object pointer cannot be nil")
}
// JSON content converting.
ok, err := doConvertWithJsonCheck(params, pointer)
if err != nil {
return err
}
if ok {
return nil
}
defer func() {
// Catch the panic, especially the reflection operation panics.
if exception := recover(); exception != nil {
if v, ok := exception.(error); ok && gerror.HasStack(v) {
err = v
} else {
err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception)
}
}
}()
var (
paramsReflectValue 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.
)
if v, ok := params.(reflect.Value); ok {
paramsReflectValue = v
} else {
paramsReflectValue = reflect.ValueOf(params)
}
paramsInterface = paramsReflectValue.Interface()
if v, ok := pointer.(reflect.Value); ok {
pointerReflectValue = v
pointerElemReflectValue = v
} else {
pointerReflectValue = reflect.ValueOf(pointer)
pointerReflectKind = pointerReflectValue.Kind()
if pointerReflectKind != reflect.Ptr {
return gerror.NewCodef(
gcode.CodeInvalidParameter,
"destination pointer should be type of '*struct', but got '%v'",
pointerReflectKind,
)
}
// Using IsNil on reflect.Ptr variable is OK.
if !pointerReflectValue.IsValid() || pointerReflectValue.IsNil() {
return gerror.NewCode(
gcode.CodeInvalidParameter,
"destination pointer cannot be nil",
)
}
pointerElemReflectValue = pointerReflectValue.Elem()
}
// If `params` and `pointer` are the same type, the do directly assignment.
// For performance enhancement purpose.
if ok = doConvertWithTypeCheck(paramsReflectValue, pointerElemReflectValue); ok {
return nil
}
// custom convert.
if ok, err = cf.callCustomConverter(paramsReflectValue, pointerReflectValue); ok {
return err
}
// Normal unmarshalling interfaces checks.
if ok, err = bindVarToReflectValueWithInterfaceCheck(pointerReflectValue, paramsInterface); ok {
return err
}
// It automatically creates struct object if necessary.
// For example, if `pointer` is **User, then `elem` is *User, which is a pointer to User.
if pointerElemReflectValue.Kind() == reflect.Ptr {
if !pointerElemReflectValue.IsValid() || pointerElemReflectValue.IsNil() {
e := reflect.New(pointerElemReflectValue.Type().Elem())
pointerElemReflectValue.Set(e)
defer func() {
if err != nil {
// If it is converted failed, it reset the `pointer` to nil.
pointerReflectValue.Elem().Set(reflect.Zero(pointerReflectValue.Type().Elem()))
}
}()
}
// if v, ok := pointerElemReflectValue.Interface().(localinterface.IUnmarshalValue); ok {
// return v.UnmarshalValue(params)
// }
// Note that it's `pointerElemReflectValue` here not `pointerReflectValue`.
if ok, err = bindVarToReflectValueWithInterfaceCheck(pointerElemReflectValue, paramsInterface); ok {
return err
}
// Retrieve its element, may be struct at last.
pointerElemReflectValue = pointerElemReflectValue.Elem()
}
paramsMap, ok := paramsInterface.(map[string]any)
if !ok {
// 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]any" failed`,
params,
)
}
}
// Nothing to be done as the parameters are empty.
if len(paramsMap) == 0 {
return nil
}
// Get struct info from cache or parse struct and cache the struct info.
cachedStructInfo := cf.internalConvertConfig.GetCachedStructInfo(
pointerElemReflectValue.Type(), priorityTag,
)
// Nothing to be converted.
if cachedStructInfo == nil {
return nil
}
// For the structure types of 0 tagOrFiledNameToFieldInfoMap,
// they also need to be cached to prevent invalid logic
if cachedStructInfo.HasNoFields() {
return nil
}
var (
// Indicates that those values have been used and cannot be reused.
usedParamsKeyOrTagNameMap = structcache.GetUsedParamsKeyOrTagNameMapFromPool()
cachedFieldInfo *structcache.CachedFieldInfo
paramsValue any
)
defer structcache.PutUsedParamsKeyOrTagNameMapToPool(usedParamsKeyOrTagNameMap)
// Firstly, search according to custom mapping rules.
// If a possible direct assignment is found, reduce the number of subsequent map searches.
for paramKey, fieldName := range paramKeyToAttrMap {
paramsValue, ok = paramsMap[paramKey]
if !ok {
continue
}
cachedFieldInfo = cachedStructInfo.GetFieldInfo(fieldName)
if cachedFieldInfo != nil {
fieldValue := cachedFieldInfo.GetFieldReflectValueFrom(pointerElemReflectValue)
if err = bindVarToStructField(
cf,
cachedFieldInfo,
fieldValue,
paramsValue,
paramKeyToAttrMap,
); err != nil {
return err
}
if len(cachedFieldInfo.OtherSameNameField) > 0 {
if err = setOtherSameNameField(
cf, cachedFieldInfo, paramsValue, pointerReflectValue, paramKeyToAttrMap,
); err != nil {
return err
}
}
usedParamsKeyOrTagNameMap[paramKey] = struct{}{}
}
}
// Already done converting for given `paramsMap`.
if len(usedParamsKeyOrTagNameMap) == len(paramsMap) {
return nil
}
return bindStructWithLoopFieldInfos(
cf, paramsMap, pointerElemReflectValue, paramKeyToAttrMap, usedParamsKeyOrTagNameMap, cachedStructInfo,
)
}
func setOtherSameNameField(
cf *ConvertConfig,
cachedFieldInfo *structcache.CachedFieldInfo,
srcValue any,
structValue reflect.Value,
paramKeyToAttrMap map[string]string,
) (err error) {
// loop the same field name of all sub attributes.
for _, otherFieldInfo := range cachedFieldInfo.OtherSameNameField {
fieldValue := cachedFieldInfo.GetOtherFieldReflectValueFrom(structValue, otherFieldInfo.FieldIndexes)
if err = bindVarToStructField(cf, otherFieldInfo, fieldValue, srcValue, paramKeyToAttrMap); err != nil {
return err
}
}
return nil
}
func bindStructWithLoopFieldInfos(
cf *ConvertConfig,
paramsMap map[string]any,
structValue reflect.Value,
paramKeyToAttrMap map[string]string,
usedParamsKeyOrTagNameMap map[string]struct{},
cachedStructInfo *structcache.CachedStructInfo,
) (err error) {
var (
cachedFieldInfo *structcache.CachedFieldInfo
fuzzLastKey string
fieldValue reflect.Value
paramKey string
paramValue any
matched bool
ok bool
)
for _, cachedFieldInfo = range cachedStructInfo.GetFieldConvertInfos() {
for _, fieldTag := range cachedFieldInfo.PriorityTagAndFieldName {
if paramValue, ok = paramsMap[fieldTag]; !ok {
continue
}
fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue)
if err = bindVarToStructField(
cf, cachedFieldInfo, fieldValue, paramValue, paramKeyToAttrMap,
); err != nil {
return err
}
// handle same field name in nested struct.
if len(cachedFieldInfo.OtherSameNameField) > 0 {
if err = setOtherSameNameField(
cf, cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap,
); err != nil {
return err
}
}
usedParamsKeyOrTagNameMap[fieldTag] = struct{}{}
matched = true
break
}
if matched {
matched = false
continue
}
fuzzLastKey = cachedFieldInfo.LastFuzzyKey.Load().(string)
if paramValue, ok = paramsMap[fuzzLastKey]; !ok {
paramKey, paramValue = fuzzyMatchingFieldName(
cachedFieldInfo.RemoveSymbolsFieldName, paramsMap, usedParamsKeyOrTagNameMap,
)
ok = paramKey != ""
cachedFieldInfo.LastFuzzyKey.Store(paramKey)
}
if ok {
fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue)
if paramValue != nil {
if err = bindVarToStructField(
cf, cachedFieldInfo, fieldValue, paramValue, paramKeyToAttrMap,
); err != nil {
return err
}
// handle same field name in nested struct.
if len(cachedFieldInfo.OtherSameNameField) > 0 {
if err = setOtherSameNameField(
cf, cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap,
); err != nil {
return err
}
}
}
usedParamsKeyOrTagNameMap[paramKey] = struct{}{}
}
}
return nil
}
// fuzzy matching rule:
// to match field name and param key in case-insensitive and without symbols.
func fuzzyMatchingFieldName(
fieldName string,
paramsMap map[string]any,
usedParamsKeyMap map[string]struct{},
) (string, any) {
for paramKey, paramValue := range paramsMap {
if _, ok := usedParamsKeyMap[paramKey]; ok {
continue
}
removeParamKeyUnderline := utils.RemoveSymbols(paramKey)
if strings.EqualFold(fieldName, removeParamKeyUnderline) {
return paramKey, paramValue
}
}
return "", nil
}
// bindVarToStructField sets value to struct object attribute by name.
// each value to attribute converting comes into in this function.
func bindVarToStructField(
cf *ConvertConfig,
cachedFieldInfo *structcache.CachedFieldInfo,
fieldValue reflect.Value,
srcValue any,
paramKeyToAttrMap map[string]string,
) (err error) {
if !fieldValue.IsValid() {
return nil
}
// CanSet checks whether attribute is public accessible.
if !fieldValue.CanSet() {
return nil
}
defer func() {
if exception := recover(); exception != nil {
if err = bindVarToReflectValue(cf, fieldValue, srcValue, paramKeyToAttrMap); err != nil {
err = gerror.Wrapf(err, `error binding srcValue to attribute "%s"`, cachedFieldInfo.FieldName())
}
}
}()
// Directly converting.
if empty.IsNil(srcValue) {
fieldValue.Set(reflect.Zero(fieldValue.Type()))
return nil
}
// Try to call custom converter.
// Issue: https://github.com/gogf/gf/issues/3099
var (
customConverterInput reflect.Value
ok bool
)
if cachedFieldInfo.IsCustomConvert {
if customConverterInput, ok = srcValue.(reflect.Value); !ok {
customConverterInput = reflect.ValueOf(srcValue)
}
if ok, err = cf.callCustomConverter(customConverterInput, fieldValue); ok || err != nil {
return
}
}
if cachedFieldInfo.IsCommonInterface {
if ok, err = bindVarToReflectValueWithInterfaceCheck(fieldValue, srcValue); ok || err != nil {
return
}
}
// Common types use fast assignment logic
if cachedFieldInfo.ConvertFunc != nil {
return cachedFieldInfo.ConvertFunc(srcValue, 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 any) (bool, error) {
var pointer any
if reflectValue.Kind() != reflect.Ptr && reflectValue.CanAddr() {
reflectValueAddr := reflectValue.Addr()
if reflectValueAddr.IsNil() || !reflectValueAddr.IsValid() {
return false, nil
}
// Not a pointer, but can token address, that makes it can be unmarshalled.
pointer = reflectValue.Addr().Interface()
} else {
if reflectValue.IsNil() || !reflectValue.IsValid() {
return false, nil
}
pointer = reflectValue.Interface()
}
// UnmarshalValue.
if v, ok := pointer.(localinterface.IUnmarshalValue); ok {
return ok, v.UnmarshalValue(value)
}
// UnmarshalText.
if v, ok := pointer.(localinterface.IUnmarshalText); ok {
var valueBytes []byte
if b, ok := value.([]byte); ok {
valueBytes = b
} else if s, ok := value.(string); ok {
valueBytes = []byte(s)
} else if f, ok := value.(localinterface.IString); ok {
valueBytes = []byte(f.String())
}
if len(valueBytes) > 0 {
return ok, v.UnmarshalText(valueBytes)
}
}
// UnmarshalJSON.
if v, ok := pointer.(localinterface.IUnmarshalJSON); ok {
var valueBytes []byte
if b, ok := value.([]byte); ok {
valueBytes = b
} else if s, ok := value.(string); ok {
valueBytes = []byte(s)
} else if f, ok := value.(localinterface.IString); ok {
valueBytes = []byte(f.String())
}
if len(valueBytes) > 0 {
// If it is not a valid JSON string, it then adds char `"` on its both sides to make it is.
if !json.Valid(valueBytes) {
newValueBytes := make([]byte, len(valueBytes)+2)
newValueBytes[0] = '"'
newValueBytes[len(newValueBytes)-1] = '"'
copy(newValueBytes[1:], valueBytes)
valueBytes = newValueBytes
}
return ok, v.UnmarshalJSON(valueBytes)
}
}
if v, ok := pointer.(localinterface.ISet); ok {
v.Set(value)
return ok, nil
}
return false, nil
}
// bindVarToReflectValue sets `value` to reflect value object `structFieldValue`.
func bindVarToReflectValue(
cf *ConvertConfig, structFieldValue reflect.Value, value any, paramKeyToAttrMap map[string]string,
) (err error) {
// JSON content converting.
ok, err := doConvertWithJsonCheck(value, structFieldValue)
if err != nil {
return err
}
if ok {
return nil
}
kind := structFieldValue.Kind()
// Converting using `Set` interface implements, for some types.
switch kind {
case reflect.Slice, reflect.Array, reflect.Ptr, reflect.Interface:
if !structFieldValue.IsNil() {
if v, ok := structFieldValue.Interface().(localinterface.ISet); ok {
v.Set(value)
return nil
}
}
}
// Converting using reflection by kind.
switch kind {
case reflect.Map:
return doMapToMap(cf, value, structFieldValue, paramKeyToAttrMap)
case reflect.Struct:
// Recursively converting for struct attribute.
if err = doStruct(value, structFieldValue, nil, ""); err != nil {
// Note there's reflect conversion mechanism here.
structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
}
// Note that the slice element might be type of struct,
// so it uses Struct function doing the converting internally.
case reflect.Slice, reflect.Array:
var (
reflectArray reflect.Value
reflectValue = reflect.ValueOf(value)
)
if reflectValue.Kind() == reflect.Slice || reflectValue.Kind() == reflect.Array {
reflectArray = reflect.MakeSlice(structFieldValue.Type(), reflectValue.Len(), reflectValue.Len())
if reflectValue.Len() > 0 {
var (
elemType = reflectArray.Index(0).Type()
elemTypeName string
converted bool
)
for i := 0; i < reflectValue.Len(); i++ {
converted = false
elemTypeName = elemType.Name()
if elemTypeName == "" {
elemTypeName = elemType.String()
}
var elem reflect.Value
if elemType.Kind() == reflect.Ptr {
elem = reflect.New(elemType.Elem()).Elem()
} else {
elem = reflect.New(elemType).Elem()
}
if elem.Kind() == reflect.Struct {
if err = doStruct(reflectValue.Index(i).Interface(), elem, nil, ""); err == nil {
converted = true
}
}
if !converted {
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.
elem = elem.Addr()
}
reflectArray.Index(i).Set(elem)
}
}
} else {
var (
elem reflect.Value
elemType = structFieldValue.Type().Elem()
elemTypeName = elemType.Name()
converted bool
)
switch reflectValue.Kind() {
case reflect.String:
// Value is empty string.
if reflectValue.IsZero() {
var elemKind = elemType.Kind()
// Try to find the original type kind of the slice element.
if elemKind == reflect.Ptr {
elemKind = elemType.Elem().Kind()
}
switch elemKind {
case reflect.String:
// Empty string cannot be assigned to string slice.
return nil
}
}
}
if elemTypeName == "" {
elemTypeName = elemType.String()
}
if elemType.Kind() == reflect.Ptr {
elem = reflect.New(elemType.Elem()).Elem()
} else {
elem = reflect.New(elemType).Elem()
}
if elem.Kind() == reflect.Struct {
if err = doStruct(value, elem, nil, ""); err == nil {
converted = true
}
}
if !converted {
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.
elem = elem.Addr()
}
reflectArray = reflect.MakeSlice(structFieldValue.Type(), 1, 1)
reflectArray.Index(0).Set(elem)
}
structFieldValue.Set(reflectArray)
case reflect.Ptr:
if structFieldValue.IsNil() || structFieldValue.IsZero() {
// Nil or empty pointer, it creates a new one.
item := reflect.New(structFieldValue.Type().Elem())
if ok, err = bindVarToReflectValueWithInterfaceCheck(item, value); ok {
structFieldValue.Set(item)
return err
}
elem := item.Elem()
if err = bindVarToReflectValue(cf, elem, value, paramKeyToAttrMap); err == nil {
structFieldValue.Set(elem.Addr())
}
} else {
// Not empty pointer, it assigns values to it.
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((*any)(nil)))
} else {
// Note there's reflect conversion mechanism here.
structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
}
default:
defer func() {
if exception := recover(); exception != nil {
err = gerror.NewCodef(
gcode.CodeInternalPanic,
`cannot convert value "%+v" to type "%s":%+v`,
value,
structFieldValue.Type().String(),
exception,
)
}
}()
// It here uses reflect converting `value` to type of the attribute and assigns
// the result value to the attribute. It might fail and panic if the usual Go
// conversion rules do not allow conversion.
structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
}
return nil
return defaultConverter.Struct(params, pointer, nil, priorityTag)
}

View File

@ -6,128 +6,20 @@
package gconv
import (
"reflect"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
)
// Structs converts any slice to given struct slice.
// Also see Scan, Struct.
func Structs(params interface{}, pointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) {
func Structs(params any, pointer any, paramKeyToAttrMap ...map[string]string) (err error) {
return Scan(params, pointer, paramKeyToAttrMap...)
}
// SliceStruct is alias of Structs.
func SliceStruct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
func SliceStruct(params any, pointer any, mapping ...map[string]string) (err error) {
return Structs(params, pointer, mapping...)
}
// StructsTag acts as Structs 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 StructsTag(params interface{}, pointer interface{}, priorityTag string) (err error) {
return doStructs(params, pointer, nil, priorityTag)
}
// doStructs converts any slice to given struct slice.
//
// It automatically checks and converts json string to []map if `params` is string/[]byte.
//
// The parameter `pointer` should be type of pointer to slice of struct.
// Note that if `pointer` is a pointer to another pointer of type of slice of struct,
// it will create the struct/pointer internally.
func doStructs(
params interface{}, pointer interface{}, paramKeyToAttrMap map[string]string, priorityTag string,
) (err error) {
defer func() {
// Catch the panic, especially the reflection operation panics.
if exception := recover(); exception != nil {
if v, ok := exception.(error); ok && gerror.HasStack(v) {
err = v
} else {
err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception)
}
}
}()
// Pointer type check.
pointerRv, ok := pointer.(reflect.Value)
if !ok {
pointerRv = reflect.ValueOf(pointer)
if kind := pointerRv.Kind(); kind != reflect.Ptr {
return gerror.NewCodef(
gcode.CodeInvalidParameter,
"pointer should be type of pointer, but got: %v", kind,
)
}
}
// Converting `params` to map slice.
var (
paramsList []interface{}
paramsRv = reflect.ValueOf(params)
paramsKind = paramsRv.Kind()
)
for paramsKind == reflect.Ptr {
paramsRv = paramsRv.Elem()
paramsKind = paramsRv.Kind()
}
switch paramsKind {
case reflect.Slice, reflect.Array:
paramsList = make([]interface{}, paramsRv.Len())
for i := 0; i < paramsRv.Len(); i++ {
paramsList[i] = paramsRv.Index(i).Interface()
}
default:
var paramsMaps = Maps(params)
paramsList = make([]interface{}, len(paramsMaps))
for i := 0; i < len(paramsMaps); i++ {
paramsList[i] = paramsMaps[i]
}
}
// If `params` is an empty slice, no conversion.
if len(paramsList) == 0 {
return nil
}
var (
reflectElemArray = reflect.MakeSlice(pointerRv.Type().Elem(), len(paramsList), len(paramsList))
itemType = reflectElemArray.Index(0).Type()
itemTypeKind = itemType.Kind()
pointerRvElem = pointerRv.Elem()
pointerRvLength = pointerRvElem.Len()
)
if itemTypeKind == reflect.Ptr {
// Pointer element.
for i := 0; i < len(paramsList); i++ {
var tempReflectValue reflect.Value
if i < pointerRvLength {
// Might be nil.
tempReflectValue = pointerRvElem.Index(i).Elem()
}
if !tempReflectValue.IsValid() {
tempReflectValue = reflect.New(itemType.Elem()).Elem()
}
if err = doStruct(paramsList[i], tempReflectValue, paramKeyToAttrMap, priorityTag); err != nil {
return err
}
reflectElemArray.Index(i).Set(tempReflectValue.Addr())
}
} else {
// Struct element.
for i := 0; i < len(paramsList); i++ {
var tempReflectValue reflect.Value
if i < pointerRvLength {
tempReflectValue = pointerRvElem.Index(i)
} else {
tempReflectValue = reflect.New(itemType).Elem()
}
if err = doStruct(paramsList[i], tempReflectValue, paramKeyToAttrMap, priorityTag); err != nil {
return err
}
reflectElemArray.Index(i).Set(tempReflectValue)
}
}
pointerRv.Elem().Set(reflectElemArray)
return nil
func StructsTag(params any, pointer any, priorityTag string) (err error) {
return defaultConverter.Structs(params, pointer, nil, priorityTag)
}

View File

@ -9,40 +9,21 @@ package gconv
import (
"time"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
// Time converts `any` to time.Time.
func Time(any interface{}, format ...string) time.Time {
// It's already this type.
if len(format) == 0 {
if v, ok := any.(time.Time); ok {
return v
}
}
if t := GTime(any, format...); t != nil {
return t.Time
}
return time.Time{}
func Time(any any, format ...string) time.Time {
t, _ := defaultConverter.Time(any, format...)
return t
}
// Duration converts `any` to time.Duration.
// If `any` is string, then it uses time.ParseDuration to convert it.
// If `any` is numeric, then it converts `any` as nanoseconds.
func Duration(any interface{}) time.Duration {
// It's already this type.
if v, ok := any.(time.Duration); ok {
return v
}
s := String(any)
if !utils.IsNumeric(s) {
d, _ := gtime.ParseDuration(s)
return d
}
return time.Duration(Int64(any))
func Duration(any any) time.Duration {
d, _ := defaultConverter.Duration(any)
return d
}
// GTime converts `any` to *gtime.Time.
@ -50,43 +31,7 @@ func Duration(any interface{}) time.Duration {
// It returns the converted value that matched the first format of the formats slice.
// If no `format` given, it converts `any` using gtime.NewFromTimeStamp if `any` is numeric,
// or using gtime.StrToTime if `any` is string.
func GTime(any interface{}, format ...string) *gtime.Time {
if empty.IsNil(any) {
return nil
}
if v, ok := any.(localinterface.IGTime); ok {
return v.GTime(format...)
}
// It's already this type.
if len(format) == 0 {
if v, ok := any.(*gtime.Time); ok {
return v
}
if t, ok := any.(time.Time); ok {
return gtime.New(t)
}
if t, ok := any.(*time.Time); ok {
return gtime.New(t)
}
}
s := String(any)
if len(s) == 0 {
return gtime.New()
}
// Priority conversion using given format.
if len(format) > 0 {
for _, item := range format {
t, err := gtime.StrToTimeFormat(s, item)
if t != nil && err == nil {
return t
}
}
return nil
}
if utils.IsNumeric(s) {
return gtime.NewFromTimeStamp(Int64(s))
} else {
t, _ := gtime.StrToTime(s)
return t
}
func GTime(any any, format ...string) *gtime.Time {
t, _ := defaultConverter.GTime(any, format...)
return t
}

View File

@ -6,181 +6,32 @@
package gconv
import (
"math"
"reflect"
"strconv"
"github.com/gogf/gf/v2/encoding/gbinary"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
// Uint converts `any` to uint.
func Uint(any any) uint {
v, _ := doUint(any)
v, _ := defaultConverter.Uint(any)
return v
}
func doUint(any any) (uint, error) {
if empty.IsNil(any) {
return 0, nil
}
if v, ok := any.(uint); ok {
return v, nil
}
v, err := doUint64(any)
return uint(v), err
}
// Uint8 converts `any` to uint8.
func Uint8(any any) uint8 {
v, _ := doUint8(any)
v, _ := defaultConverter.Uint8(any)
return v
}
func doUint8(any any) (uint8, error) {
if empty.IsNil(any) {
return 0, nil
}
if v, ok := any.(uint8); ok {
return v, nil
}
v, err := doUint64(any)
return uint8(v), err
}
// Uint16 converts `any` to uint16.
func Uint16(any any) uint16 {
v, _ := doUint16(any)
v, _ := defaultConverter.Uint16(any)
return v
}
func doUint16(any any) (uint16, error) {
if empty.IsNil(any) {
return 0, nil
}
if v, ok := any.(uint16); ok {
return v, nil
}
v, err := doUint64(any)
return uint16(v), err
}
// Uint32 converts `any` to uint32.
func Uint32(any any) uint32 {
v, _ := doUint32(any)
v, _ := defaultConverter.Uint32(any)
return v
}
func doUint32(any any) (uint32, error) {
if empty.IsNil(any) {
return 0, nil
}
if v, ok := any.(uint32); ok {
return v, nil
}
v, err := doUint64(any)
return uint32(v), err
}
// Uint64 converts `any` to uint64.
func Uint64(any any) uint64 {
v, _ := doUint64(any)
v, _ := defaultConverter.Uint64(any)
return v
}
func doUint64(any any) (uint64, error) {
if empty.IsNil(any) {
return 0, nil
}
if v, ok := any.(uint64); ok {
return v, nil
}
rv := reflect.ValueOf(any)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
val := rv.Int()
if val < 0 {
return uint64(val), gerror.NewCodef(
gcode.CodeInvalidParameter,
`cannot convert negative value "%d" to uint64`,
val,
)
}
return uint64(val), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return rv.Uint(), nil
case reflect.Uintptr:
return rv.Uint(), nil
case reflect.Float32, reflect.Float64:
val := rv.Float()
if val < 0 {
return uint64(val), gerror.NewCodef(
gcode.CodeInvalidParameter,
`cannot convert negative value "%f" to uint64`,
val,
)
}
return uint64(val), nil
case reflect.Bool:
if rv.Bool() {
return 1, nil
}
return 0, nil
case reflect.Ptr:
if rv.IsNil() {
return 0, nil
}
if f, ok := any.(localinterface.IUint64); ok {
return f.Uint64(), nil
}
return doUint64(rv.Elem().Interface())
case reflect.Slice:
if rv.Type().Elem().Kind() == reflect.Uint8 {
return gbinary.DecodeToUint64(rv.Bytes()), nil
}
return 0, gerror.NewCodef(
gcode.CodeInvalidParameter,
`unsupport slice type "%s" for converting to uint64`,
rv.Type().String(),
)
case reflect.String:
var s = rv.String()
// Hexadecimal
if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
v, err := strconv.ParseUint(s[2:], 16, 64)
if err == nil {
return v, nil
}
return 0, gerror.WrapCodef(
gcode.CodeInvalidParameter,
err,
`cannot convert hexadecimal string "%s" to uint64`,
s,
)
}
// Decimal
if v, err := strconv.ParseUint(s, 10, 64); err == nil {
return v, nil
}
// Float64
if v, err := doFloat64(any); err == nil {
if math.IsNaN(v) {
return 0, nil
}
return uint64(v), nil
}
default:
if f, ok := any.(localinterface.IUint64); ok {
return f.Uint64(), nil
}
}
return 0, gerror.NewCodef(
gcode.CodeInvalidParameter,
`unsupport value type "%s" for converting to uint64`,
reflect.TypeOf(any).String(),
)
}

View File

@ -92,7 +92,7 @@ func Benchmark_Struct_Basic(b *testing.B) {
func Benchmark_doStruct_Fields8_Basic_MapToStruct(b *testing.B) {
for i := 0; i < b.N; i++ {
doStruct(structMapFields8, structPointer8, map[string]string{}, "")
defaultConverter.Struct(structMapFields8, structPointer8, map[string]string{}, "")
}
}

View File

@ -10,9 +10,7 @@ package structcache
import (
"reflect"
"sync"
"time"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
@ -23,45 +21,28 @@ 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.
// TODO remove?
customConvertTypeMap map[reflect.Type]struct{}
// typeConverterFuncMap is used to store whether field types are registered to custom conversions
typeConverterFuncMap 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{}),
typeConverterFuncMap: make(map[reflect.Type]struct{}),
anyToTypeConvertMap: make(map[reflect.Type]AnyConvertFunc),
}
}
// RegisterCustomConvertType registers custom
func (cf *ConvertConfig) RegisterCustomConvertType(fieldType reflect.Type) {
// RegisterTypeConvertFunc registers converting function for custom type.
func (cf *ConvertConfig) RegisterTypeConvertFunc(fieldType reflect.Type) {
if fieldType.Kind() == reflect.Ptr {
fieldType = fieldType.Elem()
}
cf.customConvertTypeMap[fieldType] = struct{}{}
cf.typeConverterFuncMap[fieldType] = struct{}{}
}
// RegisterAnyConvertFunc registers custom type converting function for specified type.

View File

@ -29,7 +29,7 @@ func (cf *ConvertConfig) GetCachedStructInfo(structType reflect.Type, priorityTa
// else create one.
// it parses and generates a cache info for given struct type.
cachedStructInfo = NewCachedStructInfo(cf.customConvertTypeMap, cf.anyToTypeConvertMap)
cachedStructInfo = NewCachedStructInfo(cf.typeConverterFuncMap, cf.anyToTypeConvertMap)
var (
priorityTagArray []string
parentIndex = make([]int, 0)

View File

@ -18,11 +18,9 @@ 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{}
// typeConverterFuncMap is used to store whether field types are registered to custom conversions,
// for enhance converting performance in runtime.
typeConverterFuncMap map[reflect.Type]struct{}
// anyToTypeConvertMap for custom type converting from any to its reflect.Value.
anyToTypeConvertMap map[reflect.Type]AnyConvertFunc
@ -39,13 +37,13 @@ type CachedStructInfo struct {
// NewCachedStructInfo creates and returns a new CachedStructInfo object.
func NewCachedStructInfo(
customConvertTypeMap map[reflect.Type]struct{},
typeConverterFuncMap map[reflect.Type]struct{},
anyToTypeConvertMap map[reflect.Type]AnyConvertFunc,
) *CachedStructInfo {
return &CachedStructInfo{
tagOrFiledNameToFieldInfoMap: make(map[string]*CachedFieldInfo),
fieldConvertInfos: make([]*CachedFieldInfo, 0),
customConvertTypeMap: customConvertTypeMap,
typeConverterFuncMap: typeConverterFuncMap,
anyToTypeConvertMap: anyToTypeConvertMap,
}
}
@ -188,6 +186,6 @@ func (csi *CachedStructInfo) checkTypeHasCustomConvert(fieldType reflect.Type) b
if fieldType.Kind() == reflect.Ptr {
fieldType = fieldType.Elem()
}
_, ok := csi.customConvertTypeMap[fieldType]
_, ok := csi.typeConverterFuncMap[fieldType]
return ok
}