mirror of
https://gitee.com/johng/gf
synced 2026-06-29 02:26:29 +08:00
up
This commit is contained in:
@ -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)
|
||||
}
|
||||
|
||||
@ -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:(Map,Array,Slice,Struct) 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
74
util/gconv/gconv_converter_bool.go
Normal file
74
util/gconv/gconv_converter_bool.go
Normal 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:(Map,Array,Slice,Struct) 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
|
||||
67
util/gconv/gconv_converter_bytes.go
Normal file
67
util/gconv/gconv_converter_bytes.go
Normal 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
|
||||
}
|
||||
}
|
||||
145
util/gconv/gconv_converter_float.go
Normal file
145
util/gconv/gconv_converter_float.go
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
152
util/gconv/gconv_converter_int.go
Normal file
152
util/gconv/gconv_converter_int.go
Normal 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),
|
||||
)
|
||||
}
|
||||
504
util/gconv/gconv_converter_map.go
Normal file
504
util/gconv/gconv_converter_map.go
Normal 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
|
||||
}
|
||||
7
util/gconv/gconv_converter_maps.go
Normal file
7
util/gconv/gconv_converter_maps.go
Normal 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
|
||||
126
util/gconv/gconv_converter_maptomap.go
Normal file
126
util/gconv/gconv_converter_maptomap.go
Normal 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
|
||||
}
|
||||
120
util/gconv/gconv_converter_maptomaps.go
Normal file
120
util/gconv/gconv_converter_maptomaps.go
Normal 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
|
||||
}
|
||||
29
util/gconv/gconv_converter_rune.go
Normal file
29
util/gconv/gconv_converter_rune.go
Normal 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
|
||||
}
|
||||
336
util/gconv/gconv_converter_scan.go
Normal file
336
util/gconv/gconv_converter_scan.go
Normal 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
|
||||
}
|
||||
135
util/gconv/gconv_converter_string.go
Normal file
135
util/gconv/gconv_converter_string.go
Normal 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
|
||||
}
|
||||
}
|
||||
628
util/gconv/gconv_converter_struct.go
Normal file
628
util/gconv/gconv_converter_struct.go
Normal 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
|
||||
}
|
||||
115
util/gconv/gconv_converter_structs.go
Normal file
115
util/gconv/gconv_converter_structs.go
Normal 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
|
||||
}
|
||||
111
util/gconv/gconv_converter_time.go
Normal file
111
util/gconv/gconv_converter_time.go
Normal 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)
|
||||
}
|
||||
}
|
||||
156
util/gconv/gconv_converter_uint.go
Normal file
156
util/gconv/gconv_converter_uint.go
Normal 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(),
|
||||
)
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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),
|
||||
)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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...)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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(),
|
||||
)
|
||||
}
|
||||
|
||||
@ -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{}, "")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user