mirror of
https://github.com/gogf/gf.git
synced 2025-04-05 03:05:05 +08:00
624 lines
16 KiB
Go
624 lines
16 KiB
Go
// 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 converter
|
|
|
|
import (
|
|
"reflect"
|
|
"time"
|
|
|
|
"github.com/gogf/gf/v2/internal/json"
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|
)
|
|
|
|
// ConvertOption is the option for converting.
|
|
type ConvertOption struct {
|
|
// ExtraParams are extra values for implementing the converting.
|
|
ExtraParams []any
|
|
SliceOption SliceOption
|
|
MapOption MapOption
|
|
StructOption StructOption
|
|
}
|
|
|
|
func (c *Converter) getConvertOption(option ...ConvertOption) ConvertOption {
|
|
if len(option) > 0 {
|
|
return option[0]
|
|
}
|
|
return ConvertOption{}
|
|
}
|
|
|
|
// ConvertWithTypeName converts the variable `fromValue` to the type `toTypeName`, the type `toTypeName` is specified by string.
|
|
func (c *Converter) ConvertWithTypeName(fromValue any, toTypeName string, option ...ConvertOption) (any, error) {
|
|
return c.doConvert(
|
|
doConvertInput{
|
|
FromValue: fromValue,
|
|
ToTypeName: toTypeName,
|
|
ReferValue: nil,
|
|
},
|
|
c.getConvertOption(option...),
|
|
)
|
|
}
|
|
|
|
// ConvertWithRefer converts the variable `fromValue` to the type referred by value `referValue`.
|
|
func (c *Converter) ConvertWithRefer(fromValue, referValue any, option ...ConvertOption) (any, error) {
|
|
var referValueRf reflect.Value
|
|
if v, ok := referValue.(reflect.Value); ok {
|
|
referValueRf = v
|
|
} else {
|
|
referValueRf = reflect.ValueOf(referValue)
|
|
}
|
|
return c.doConvert(
|
|
doConvertInput{
|
|
FromValue: fromValue,
|
|
ToTypeName: referValueRf.Type().String(),
|
|
ReferValue: referValue,
|
|
},
|
|
c.getConvertOption(option...),
|
|
)
|
|
}
|
|
|
|
type doConvertInput struct {
|
|
FromValue any // Value that is converted from.
|
|
ToTypeName string // Target value type name in string.
|
|
ReferValue any // Referred value, a value in type `ToTypeName`. Note that its type might be reflect.Value.
|
|
|
|
// 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, option ConvertOption) (convertedValue any, err error) {
|
|
switch in.ToTypeName {
|
|
case "int":
|
|
return c.Int(in.FromValue)
|
|
case "*int":
|
|
if _, ok := in.FromValue.(*int); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Int(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "int8":
|
|
return c.Int8(in.FromValue)
|
|
case "*int8":
|
|
if _, ok := in.FromValue.(*int8); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Int8(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "int16":
|
|
return c.Int16(in.FromValue)
|
|
case "*int16":
|
|
if _, ok := in.FromValue.(*int16); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Int16(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "int32":
|
|
return c.Int32(in.FromValue)
|
|
case "*int32":
|
|
if _, ok := in.FromValue.(*int32); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Int32(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "int64":
|
|
return c.Int64(in.FromValue)
|
|
case "*int64":
|
|
if _, ok := in.FromValue.(*int64); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Int64(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "uint":
|
|
return c.Uint(in.FromValue)
|
|
case "*uint":
|
|
if _, ok := in.FromValue.(*uint); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Uint(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "uint8":
|
|
return c.Uint8(in.FromValue)
|
|
case "*uint8":
|
|
if _, ok := in.FromValue.(*uint8); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Uint8(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "uint16":
|
|
return c.Uint16(in.FromValue)
|
|
case "*uint16":
|
|
if _, ok := in.FromValue.(*uint16); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Uint16(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "uint32":
|
|
return c.Uint32(in.FromValue)
|
|
case "*uint32":
|
|
if _, ok := in.FromValue.(*uint32); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Uint32(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "uint64":
|
|
return c.Uint64(in.FromValue)
|
|
case "*uint64":
|
|
if _, ok := in.FromValue.(*uint64); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Uint64(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "float32":
|
|
return c.Float32(in.FromValue)
|
|
case "*float32":
|
|
if _, ok := in.FromValue.(*float32); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Float32(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "float64":
|
|
return c.Float64(in.FromValue)
|
|
case "*float64":
|
|
if _, ok := in.FromValue.(*float64); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Float64(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "bool":
|
|
return c.Bool(in.FromValue)
|
|
case "*bool":
|
|
if _, ok := in.FromValue.(*bool); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Bool(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "string":
|
|
return c.String(in.FromValue)
|
|
case "*string":
|
|
if _, ok := in.FromValue.(*string); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.String(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "[]byte":
|
|
return c.Bytes(in.FromValue)
|
|
case "[]int":
|
|
return c.SliceInt(in.FromValue, option.SliceOption)
|
|
case "[]int32":
|
|
return c.SliceInt32(in.FromValue, option.SliceOption)
|
|
case "[]int64":
|
|
return c.SliceInt64(in.FromValue, option.SliceOption)
|
|
case "[]uint":
|
|
return c.SliceUint(in.FromValue, option.SliceOption)
|
|
case "[]uint8":
|
|
return c.Bytes(in.FromValue)
|
|
case "[]uint32":
|
|
return c.SliceUint32(in.FromValue, option.SliceOption)
|
|
case "[]uint64":
|
|
return c.SliceUint64(in.FromValue, option.SliceOption)
|
|
case "[]float32":
|
|
return c.SliceFloat32(in.FromValue, option.SliceOption)
|
|
case "[]float64":
|
|
return c.SliceFloat64(in.FromValue, option.SliceOption)
|
|
case "[]string":
|
|
return c.SliceStr(in.FromValue, option.SliceOption)
|
|
|
|
case "Time", "time.Time":
|
|
if len(option.ExtraParams) > 0 {
|
|
s, err := c.String(option.ExtraParams[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return c.Time(in.FromValue, s)
|
|
}
|
|
return c.Time(in.FromValue)
|
|
case "*time.Time":
|
|
var v time.Time
|
|
if len(option.ExtraParams) > 0 {
|
|
s, err := c.String(option.ExtraParams[0])
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
v, err = c.Time(in.FromValue, s)
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
} else {
|
|
if _, ok := in.FromValue.(*time.Time); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err = c.Time(in.FromValue)
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
}
|
|
return &v, nil
|
|
|
|
case "GTime", "gtime.Time":
|
|
if len(option.ExtraParams) > 0 {
|
|
s, err := c.String(option.ExtraParams[0])
|
|
if err != nil {
|
|
return *gtime.New(), err
|
|
}
|
|
v, err := c.GTime(in.FromValue, s)
|
|
if err != nil {
|
|
return *gtime.New(), err
|
|
}
|
|
if v != nil {
|
|
return *v, nil
|
|
}
|
|
return *gtime.New(), nil
|
|
}
|
|
v, err := c.GTime(in.FromValue)
|
|
if err != nil {
|
|
return *gtime.New(), err
|
|
}
|
|
if v != nil {
|
|
return *v, nil
|
|
}
|
|
return *gtime.New(), nil
|
|
case "*gtime.Time":
|
|
if len(option.ExtraParams) > 0 {
|
|
s, err := c.String(option.ExtraParams[0])
|
|
if err != nil {
|
|
return gtime.New(), err
|
|
}
|
|
v, err := c.GTime(in.FromValue, s)
|
|
if err != nil {
|
|
return gtime.New(), err
|
|
}
|
|
if v != nil {
|
|
return v, nil
|
|
}
|
|
return gtime.New(), nil
|
|
}
|
|
v, err := c.GTime(in.FromValue)
|
|
if err != nil {
|
|
return gtime.New(), err
|
|
}
|
|
if v != nil {
|
|
return v, nil
|
|
}
|
|
return gtime.New(), nil
|
|
|
|
case "Duration", "time.Duration":
|
|
return c.Duration(in.FromValue)
|
|
case "*time.Duration":
|
|
if _, ok := in.FromValue.(*time.Duration); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Duration(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "map[string]string":
|
|
return c.MapStrStr(in.FromValue, option.MapOption)
|
|
|
|
case "map[string]interface {}":
|
|
return c.Map(in.FromValue, option.MapOption)
|
|
|
|
case "[]map[string]interface {}":
|
|
return c.SliceMap(in.FromValue, SliceMapOption{
|
|
SliceOption: option.SliceOption,
|
|
MapOption: option.MapOption,
|
|
})
|
|
|
|
case "RawMessage", "json.RawMessage":
|
|
// issue 3449
|
|
bytes, err := json.Marshal(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return bytes, nil
|
|
|
|
default:
|
|
return c.doConvertForDefault(in, option)
|
|
}
|
|
}
|
|
|
|
func (c *Converter) doConvertForDefault(in doConvertInput, option ConvertOption) (convertedValue any, err error) {
|
|
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.
|
|
var (
|
|
ok bool
|
|
dstReflectValue reflect.Value
|
|
)
|
|
dstReflectValue, ok, err = c.callCustomConverterWithRefer(fromReflectValue, referReflectValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ok {
|
|
return dstReflectValue.Interface(), nil
|
|
}
|
|
|
|
defer func() {
|
|
if recover() != nil {
|
|
in.alreadySetToReferValue = false
|
|
if err = c.bindVarToReflectValue(referReflectValue, in.FromValue, option.StructOption); err == nil {
|
|
in.alreadySetToReferValue = true
|
|
convertedValue = referReflectValue.Interface()
|
|
}
|
|
}
|
|
}()
|
|
switch referReflectValue.Kind() {
|
|
case reflect.Ptr:
|
|
// Type converting for custom type pointers.
|
|
// Example:
|
|
// 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
|
|
result, err := c.doConvert(in, option)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
refElementValue := reflect.ValueOf(result)
|
|
originTypeValue := reflect.New(refElementValue.Type()).Elem()
|
|
originTypeValue.Set(refElementValue)
|
|
in.alreadySetToReferValue = true
|
|
return originTypeValue.Addr().Convert(referReflectValue.Type()).Interface(), nil
|
|
}
|
|
|
|
case reflect.Map:
|
|
var targetValue = reflect.New(referReflectValue.Type()).Elem()
|
|
if err = c.MapToMap(in.FromValue, targetValue, nil, option.MapOption); err == nil {
|
|
in.alreadySetToReferValue = true
|
|
}
|
|
return targetValue.Interface(), nil
|
|
|
|
default:
|
|
}
|
|
in.ToTypeName = referReflectValue.Kind().String()
|
|
in.ReferValue = nil
|
|
in.alreadySetToReferValue = true
|
|
result, err := c.doConvert(in, option)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
convertedValue = reflect.ValueOf(result).Convert(referReflectValue.Type()).Interface()
|
|
return convertedValue, nil
|
|
}
|
|
return in.FromValue, nil
|
|
}
|
|
|
|
func (c *Converter) doConvertWithReflectValueSet(reflectValue reflect.Value, in doConvertInput, option ConvertOption) error {
|
|
convertedValue, err := c.doConvert(in, option)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !in.alreadySetToReferValue {
|
|
reflectValue.Set(reflect.ValueOf(convertedValue))
|
|
}
|
|
return err
|
|
}
|
|
|
|
// callCustomConverter call the custom converter. It will try some possible type.
|
|
func (c *Converter) callCustomConverter(srcReflectValue, dstReflectValue reflect.Value) (converted bool, err error) {
|
|
// search type converter function.
|
|
registeredConverterFunc, srcType, ok := c.getRegisteredTypeConverterFuncAndSrcType(srcReflectValue, dstReflectValue)
|
|
if ok {
|
|
return c.doCallCustomTypeConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType)
|
|
}
|
|
|
|
// search any converter function.
|
|
anyConverterFunc := c.getRegisteredAnyConverterFunc(dstReflectValue)
|
|
if anyConverterFunc == nil {
|
|
return false, nil
|
|
}
|
|
err = anyConverterFunc(srcReflectValue.Interface(), dstReflectValue)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
func (c *Converter) callCustomConverterWithRefer(
|
|
srcReflectValue, referReflectValue reflect.Value,
|
|
) (dstReflectValue reflect.Value, converted bool, err error) {
|
|
// search type converter function.
|
|
registeredConverterFunc, srcType, ok := c.getRegisteredTypeConverterFuncAndSrcType(
|
|
srcReflectValue, referReflectValue,
|
|
)
|
|
if ok {
|
|
dstReflectValue = reflect.New(referReflectValue.Type()).Elem()
|
|
converted, err = c.doCallCustomTypeConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType)
|
|
return
|
|
}
|
|
|
|
// search any converter function.
|
|
anyConverterFunc := c.getRegisteredAnyConverterFunc(referReflectValue)
|
|
if anyConverterFunc == nil {
|
|
return reflect.Value{}, false, nil
|
|
}
|
|
dstReflectValue = reflect.New(referReflectValue.Type()).Elem()
|
|
err = anyConverterFunc(srcReflectValue.Interface(), dstReflectValue)
|
|
if err != nil {
|
|
return reflect.Value{}, false, err
|
|
}
|
|
return dstReflectValue, true, nil
|
|
}
|
|
|
|
func (c *Converter) getRegisteredTypeConverterFuncAndSrcType(
|
|
srcReflectValue, dstReflectValueForRefer reflect.Value,
|
|
) (f converterFunc, srcType reflect.Type, ok bool) {
|
|
if len(c.typeConverterFuncMap) == 0 {
|
|
return reflect.Value{}, nil, false
|
|
}
|
|
srcType = srcReflectValue.Type()
|
|
for srcType.Kind() == reflect.Pointer {
|
|
srcType = srcType.Elem()
|
|
}
|
|
var registeredOutTypeMap map[converterOutType]converterFunc
|
|
// firstly, it searches the map by input parameter type.
|
|
registeredOutTypeMap, ok = c.typeConverterFuncMap[srcType]
|
|
if !ok {
|
|
return reflect.Value{}, nil, false
|
|
}
|
|
var dstType = dstReflectValueForRefer.Type()
|
|
if dstType.Kind() == reflect.Pointer {
|
|
// Might be **struct, which is support as designed.
|
|
if dstType.Elem().Kind() == reflect.Pointer {
|
|
dstType = dstType.Elem()
|
|
}
|
|
} else if dstReflectValueForRefer.IsValid() && dstReflectValueForRefer.CanAddr() {
|
|
dstType = dstReflectValueForRefer.Addr().Type()
|
|
} else {
|
|
dstType = reflect.PointerTo(dstType)
|
|
}
|
|
// secondly, it searches the input parameter type map
|
|
// and finds the result converter function by the output parameter type.
|
|
f, ok = registeredOutTypeMap[dstType]
|
|
if !ok {
|
|
return reflect.Value{}, nil, false
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *Converter) getRegisteredAnyConverterFunc(dstReflectValueForRefer reflect.Value) (f AnyConvertFunc) {
|
|
if c.internalConverter.IsAnyConvertFuncEmpty() {
|
|
return nil
|
|
}
|
|
if !dstReflectValueForRefer.IsValid() {
|
|
return nil
|
|
}
|
|
var dstType = dstReflectValueForRefer.Type()
|
|
if dstType.Kind() == reflect.Pointer {
|
|
// Might be **struct, which is support as designed.
|
|
if dstType.Elem().Kind() == reflect.Pointer {
|
|
dstType = dstType.Elem()
|
|
}
|
|
} else if dstReflectValueForRefer.IsValid() && dstReflectValueForRefer.CanAddr() {
|
|
dstType = dstReflectValueForRefer.Addr().Type()
|
|
} else {
|
|
dstType = reflect.PointerTo(dstType)
|
|
}
|
|
return c.internalConverter.GetAnyConvertFuncByType(dstType)
|
|
}
|
|
|
|
func (c *Converter) doCallCustomTypeConverter(
|
|
srcReflectValue reflect.Value,
|
|
dstReflectValue reflect.Value,
|
|
registeredConverterFunc converterFunc,
|
|
srcType reflect.Type,
|
|
) (converted bool, err error) {
|
|
// Converter function calling.
|
|
for srcReflectValue.Type() != srcType {
|
|
srcReflectValue = srcReflectValue.Elem()
|
|
}
|
|
result := registeredConverterFunc.Call([]reflect.Value{srcReflectValue})
|
|
if !result[1].IsNil() {
|
|
return false, result[1].Interface().(error)
|
|
}
|
|
// The `result[0]` is a pointer.
|
|
if result[0].IsNil() {
|
|
return false, nil
|
|
}
|
|
var resultValue = result[0]
|
|
for {
|
|
if resultValue.Type() == dstReflectValue.Type() && dstReflectValue.CanSet() {
|
|
dstReflectValue.Set(resultValue)
|
|
converted = true
|
|
} else if dstReflectValue.Kind() == reflect.Pointer {
|
|
if resultValue.Type() == dstReflectValue.Elem().Type() && dstReflectValue.Elem().CanSet() {
|
|
dstReflectValue.Elem().Set(resultValue)
|
|
converted = true
|
|
}
|
|
}
|
|
if converted {
|
|
break
|
|
}
|
|
if resultValue.Kind() == reflect.Pointer {
|
|
resultValue = resultValue.Elem()
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
return converted, nil
|
|
}
|