完善数据类型转换
This commit is contained in:
@@ -31,15 +31,20 @@ var (
|
||||
reflect.Slice: convertSlice,
|
||||
reflect.Array: convertArray,
|
||||
reflect.Map: convertMap,
|
||||
reflect.Interface: convertInterface,
|
||||
}
|
||||
)
|
||||
|
||||
// 转换为字符串
|
||||
func convertToString(item interface{}) string {
|
||||
if str, ok := item.(string); ok {
|
||||
return str
|
||||
}
|
||||
switch v := item.(type) {
|
||||
case string:
|
||||
return v
|
||||
case json.Number:
|
||||
return string(v)
|
||||
default:
|
||||
return fmt.Sprintf("%v", item)
|
||||
}
|
||||
}
|
||||
|
||||
// 转换为数字(float64)
|
||||
@@ -47,10 +52,28 @@ func convertToFloat64(item interface{}) (float64, error) {
|
||||
switch v := item.(type) {
|
||||
case float64:
|
||||
return v, nil
|
||||
case int, int32, int64:
|
||||
return float64(reflect.ValueOf(v).Int()), nil
|
||||
case uint, uint32, uint64:
|
||||
return float64(reflect.ValueOf(v).Uint()), nil
|
||||
case json.Number:
|
||||
return v.Float64()
|
||||
case int:
|
||||
return float64(v), nil
|
||||
case int8:
|
||||
return float64(v), nil
|
||||
case int16:
|
||||
return float64(v), nil
|
||||
case int32:
|
||||
return float64(v), nil
|
||||
case int64:
|
||||
return float64(v), nil
|
||||
case uint:
|
||||
return float64(v), nil
|
||||
case uint8:
|
||||
return float64(v), nil
|
||||
case uint16:
|
||||
return float64(v), nil
|
||||
case uint32:
|
||||
return float64(v), nil
|
||||
case uint64:
|
||||
return float64(v), nil
|
||||
case float32:
|
||||
return float64(v), nil
|
||||
default:
|
||||
@@ -125,25 +148,40 @@ func convertString(field reflect.Value, value string) (interface{}, error) {
|
||||
}
|
||||
|
||||
func convertSlice(field reflect.Value, value string) (interface{}, error) {
|
||||
var result []interface{}
|
||||
if err := json.Unmarshal([]byte(value), &result); err != nil {
|
||||
sliceType := field.Type()
|
||||
result := reflect.New(sliceType).Elem()
|
||||
if err := json.Unmarshal([]byte(value), result.Addr().Interface()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
return result.Interface(), nil
|
||||
}
|
||||
|
||||
func convertArray(field reflect.Value, value string) (interface{}, error) {
|
||||
var result []interface{}
|
||||
arrayType := field.Type()
|
||||
result := reflect.New(arrayType).Elem()
|
||||
if err := json.Unmarshal([]byte(value), result.Addr().Interface()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result.Interface(), nil
|
||||
}
|
||||
|
||||
func convertMap(field reflect.Value, value string) (interface{}, error) {
|
||||
mapType := field.Type()
|
||||
result := reflect.New(mapType).Elem()
|
||||
if err := json.Unmarshal([]byte(value), result.Addr().Interface()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result.Interface(), nil
|
||||
}
|
||||
|
||||
func convertInterface(field reflect.Value, value string) (interface{}, error) {
|
||||
var result interface{}
|
||||
if err := json.Unmarshal([]byte(value), &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func convertMap(field reflect.Value, value string) (interface{}, error) {
|
||||
return nil, fmt.Errorf("map转换未实现")
|
||||
}
|
||||
|
||||
func getBaseTypeFromAlias(aliasType reflect.Type) reflect.Type {
|
||||
if aliasType.Kind() == reflect.Ptr {
|
||||
aliasType = aliasType.Elem()
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
|
||||
// ChangeInfo 变更信息
|
||||
type ChangeInfo struct {
|
||||
Old string `json:"old"`
|
||||
New string `json:"new"`
|
||||
Old any `json:"old"`
|
||||
New any `json:"new"`
|
||||
Val any `json:"val"`
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ type FieldInfo struct {
|
||||
var (
|
||||
basicStructTypes = map[string]bool{
|
||||
"time.Time": true,
|
||||
"time.Duration": true,
|
||||
"github.com/shopspring/decimal.Decimal": true,
|
||||
"sql.NullString": true,
|
||||
"sql.NullInt64": true,
|
||||
|
||||
+23
-4
@@ -28,10 +28,14 @@ func (dm *defaultFieldMapper) GetFieldMap(t reflect.Type) map[string]FieldInfo {
|
||||
}
|
||||
cacheMutex.RUnlock()
|
||||
|
||||
cacheMutex.Lock()
|
||||
// 双重检查:避免两个goroutine同时构建
|
||||
if cached, exists := typeInfoCache[t]; exists {
|
||||
cacheMutex.Unlock()
|
||||
return cached
|
||||
}
|
||||
fieldMap := make(map[string]FieldInfo)
|
||||
dm.buildFieldMapRecursive(t, []int{}, fieldMap, "")
|
||||
|
||||
cacheMutex.Lock()
|
||||
typeInfoCache[t] = fieldMap
|
||||
cacheMutex.Unlock()
|
||||
|
||||
@@ -40,6 +44,16 @@ func (dm *defaultFieldMapper) GetFieldMap(t reflect.Type) map[string]FieldInfo {
|
||||
|
||||
// 递归构建字段映射表
|
||||
func (dm *defaultFieldMapper) buildFieldMapRecursive(t reflect.Type, index []int, fieldMap map[string]FieldInfo, prefix string) {
|
||||
dm.buildFieldMapRecursiveWithVisited(t, index, fieldMap, prefix, make(map[reflect.Type]bool))
|
||||
}
|
||||
|
||||
func (dm *defaultFieldMapper) buildFieldMapRecursiveWithVisited(t reflect.Type, index []int, fieldMap map[string]FieldInfo, prefix string, stack map[reflect.Type]bool) {
|
||||
if stack[t] {
|
||||
return
|
||||
}
|
||||
stack[t] = true
|
||||
defer delete(stack, t)
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
if !field.IsExported() {
|
||||
@@ -58,10 +72,15 @@ func (dm *defaultFieldMapper) buildFieldMapRecursive(t reflect.Type, index []int
|
||||
isPtr := fieldType.Kind() == reflect.Ptr
|
||||
actualType := fieldType
|
||||
if isPtr {
|
||||
// 解引用
|
||||
actualType = fieldType.Elem()
|
||||
}
|
||||
|
||||
// 嵌入式结构体字段提升——其字段注册到父级别
|
||||
if field.Anonymous && actualType.Kind() == reflect.Struct && !isBasicStructType(actualType) {
|
||||
dm.buildFieldMapRecursiveWithVisited(actualType, currentIndex, fieldMap, prefix, stack)
|
||||
continue
|
||||
}
|
||||
|
||||
if actualType.Kind() == reflect.Slice || actualType.Kind() == reflect.Array {
|
||||
fieldMap[fullKey] = FieldInfo{
|
||||
Index: currentIndex,
|
||||
@@ -75,7 +94,7 @@ func (dm *defaultFieldMapper) buildFieldMapRecursive(t reflect.Type, index []int
|
||||
}
|
||||
|
||||
if actualType.Kind() == reflect.Struct && !isBasicStructType(actualType) {
|
||||
dm.buildFieldMapRecursive(actualType, currentIndex, fieldMap, fullKey)
|
||||
dm.buildFieldMapRecursiveWithVisited(actualType, currentIndex, fieldMap, fullKey, stack)
|
||||
}
|
||||
|
||||
fieldMap[fullKey] = FieldInfo{
|
||||
|
||||
+31
-14
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
@@ -120,16 +121,15 @@ func (sp *StructProcessor) AttactToStruct(structxx any, updateMap map[string]str
|
||||
continue
|
||||
}
|
||||
|
||||
oldValueStr, _ := cast.ToStringE(field.Interface())
|
||||
oldValue := field.Interface()
|
||||
newValue, err := sp.valueSetter.SetFieldValue(field, mapValue)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("设置字段 %s 的值失败: %w", mapKey, err)
|
||||
}
|
||||
|
||||
newValueStr, _ := cast.ToStringE(newValue)
|
||||
changeMap[mapKey] = ChangeInfo{
|
||||
Old: oldValueStr,
|
||||
New: newValueStr,
|
||||
Old: oldValue,
|
||||
New: newValue,
|
||||
Val: newValue,
|
||||
}
|
||||
}
|
||||
@@ -148,15 +148,14 @@ func (sp *StructProcessor) getFieldByPath(v reflect.Value, index []int) (reflect
|
||||
return reflect.Value{}, fmt.Errorf("字段索引 %d 无效", index[0])
|
||||
}
|
||||
|
||||
// 处理指针
|
||||
if len(index) > 1 {
|
||||
// 只有中间节点才自动解引用指针
|
||||
for field.Kind() == reflect.Ptr {
|
||||
if field.IsNil() {
|
||||
field.Set(reflect.New(field.Type().Elem()))
|
||||
}
|
||||
field = field.Elem()
|
||||
}
|
||||
|
||||
if len(index) > 1 {
|
||||
return sp.getFieldByPath(field, index[1:])
|
||||
}
|
||||
|
||||
@@ -166,7 +165,9 @@ func (sp *StructProcessor) getFieldByPath(v reflect.Value, index []int) (reflect
|
||||
// processSliceOrArrayField 处理切片和数组字段
|
||||
func (sp *StructProcessor) processSliceOrArrayField(field reflect.Value, value string, fieldKey string) error {
|
||||
var jsonData []interface{}
|
||||
if err := json.Unmarshal([]byte(value), &jsonData); err != nil {
|
||||
decoder := json.NewDecoder(strings.NewReader(value))
|
||||
decoder.UseNumber()
|
||||
if err := decoder.Decode(&jsonData); err != nil {
|
||||
return fmt.Errorf("字段 %s 的值必须是JSON数组格式: %w", fieldKey, err)
|
||||
}
|
||||
|
||||
@@ -215,7 +216,9 @@ func (sp *StructProcessor) processNestedStruct(field reflect.Value, value string
|
||||
|
||||
// 尝试解析为JSON对象
|
||||
var nestedMap map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(value), &nestedMap); err == nil {
|
||||
decoder := json.NewDecoder(strings.NewReader(value))
|
||||
decoder.UseNumber()
|
||||
if err := decoder.Decode(&nestedMap); err == nil {
|
||||
stringMap := make(map[string]string, len(nestedMap))
|
||||
for k, v := range nestedMap {
|
||||
str, err := cast.ToStringE(v)
|
||||
@@ -237,17 +240,31 @@ func (sp *StructProcessor) processNestedStruct(field reflect.Value, value string
|
||||
}
|
||||
|
||||
// 尝试直接设置值
|
||||
if hasUnmarshalJSON(structValue.Type()) || isBasicStructType(structValue.Type()) {
|
||||
oldValueStr, _ := cast.ToStringE(field.Interface())
|
||||
if hasUnmarshalJSON(structValue.Type()) {
|
||||
oldValue := field.Interface()
|
||||
|
||||
if err := setUnmarshalJSONValue(structValue, value); err != nil {
|
||||
return nil, fmt.Errorf("设置UnmarshalJSON值失败: %w", err)
|
||||
}
|
||||
|
||||
changeMap[parentKey] = ChangeInfo{
|
||||
Old: oldValue,
|
||||
New: structValue.Interface(),
|
||||
Val: structValue.Interface(),
|
||||
}
|
||||
return changeMap, nil
|
||||
}
|
||||
|
||||
if isBasicStructType(structValue.Type()) {
|
||||
oldValue := field.Interface()
|
||||
|
||||
if err := setBasicStructValue(structValue, value); err != nil {
|
||||
return nil, fmt.Errorf("设置基本结构体值失败: %w", err)
|
||||
}
|
||||
|
||||
newValueStr, _ := cast.ToStringE(field.Interface())
|
||||
changeMap[parentKey] = ChangeInfo{
|
||||
Old: oldValueStr,
|
||||
New: newValueStr,
|
||||
Old: oldValue,
|
||||
New: structValue.Interface(),
|
||||
Val: structValue.Interface(),
|
||||
}
|
||||
return changeMap, nil
|
||||
|
||||
+1183
-42
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 工具函数
|
||||
@@ -39,10 +40,22 @@ func hasUnmarshalText(t reflect.Type) bool {
|
||||
|
||||
// 设置json.Unmarshaler接口的值
|
||||
func setUnmarshalJSONValue(field reflect.Value, value interface{}) error {
|
||||
jsonBytes, err := json.Marshal(value)
|
||||
var jsonBytes []byte
|
||||
if str, ok := value.(string); ok {
|
||||
// 尝试直接将字符串作为JSON解析;如果是合法JSON则直接使用
|
||||
if json.Valid([]byte(str)) {
|
||||
jsonBytes = []byte(str)
|
||||
} else {
|
||||
// 不是合法JSON,作为JSON字符串值处理(加引号)
|
||||
jsonBytes = []byte(`"` + str + `"`)
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
jsonBytes, err = json.Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var fieldAddr reflect.Value
|
||||
if field.CanAddr() {
|
||||
@@ -91,6 +104,16 @@ func setBasicStructValue(field reflect.Value, value string) error {
|
||||
if hasUnmarshalText(field.Type()) {
|
||||
return setUnmarshalTextValue(field, value)
|
||||
}
|
||||
|
||||
if field.Type().String() == "time.Duration" {
|
||||
d, err := time.ParseDuration(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
field.SetInt(int64(d))
|
||||
return nil
|
||||
}
|
||||
|
||||
return json.Unmarshal([]byte(value), field.Addr().Interface())
|
||||
}
|
||||
|
||||
@@ -111,6 +134,9 @@ func isTypeAlias(t reflect.Type) bool {
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
if basicStructTypes[t.String()] {
|
||||
return false
|
||||
}
|
||||
return t.PkgPath() != "" && basicKinds[t.Kind()]
|
||||
}
|
||||
|
||||
@@ -158,17 +184,7 @@ func setCustomTypeValue(field reflect.Value, value string) (interface{}, error)
|
||||
return field.Interface(), nil
|
||||
}
|
||||
|
||||
var jsonData interface{}
|
||||
if err := json.Unmarshal([]byte(value), &jsonData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jsonBytes, err := json.Marshal(jsonData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(jsonBytes, field.Addr().Interface()); err != nil {
|
||||
if err := json.Unmarshal([]byte(value), field.Addr().Interface()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
+23
-7
@@ -3,6 +3,7 @@ package structx
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ValueSetter 值设置器接口
|
||||
@@ -35,6 +36,13 @@ func (ds *defaultValueSetter) SetFieldValue(field reflect.Value, value string) (
|
||||
return setPointerFieldValue(field, value)
|
||||
}
|
||||
|
||||
if isBasicStructType(fieldType) {
|
||||
if err := setBasicStructValue(field, value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return field.Interface(), nil
|
||||
}
|
||||
|
||||
if isTypeAlias(fieldType) {
|
||||
return setTypeAliasValue(field, value)
|
||||
}
|
||||
@@ -73,28 +81,36 @@ func (ds *defaultValueSetter) SetSliceElementValue(elemValue reflect.Value, item
|
||||
case reflect.String:
|
||||
elemValue.SetString(convertToString(item))
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
num, err := convertToFloat64(item)
|
||||
itemStr := convertToString(item)
|
||||
intVal, err := strconv.ParseInt(itemStr, 10, elemType.Bits())
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法转换为整型: %w", err)
|
||||
}
|
||||
elemValue.SetInt(int64(num))
|
||||
elemValue.SetInt(intVal)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
num, err := convertToFloat64(item)
|
||||
itemStr := convertToString(item)
|
||||
uintVal, err := strconv.ParseUint(itemStr, 10, elemType.Bits())
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法转换为无符号整型: %w", err)
|
||||
}
|
||||
elemValue.SetUint(uint64(num))
|
||||
elemValue.SetUint(uintVal)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
num, err := convertToFloat64(item)
|
||||
itemStr := convertToString(item)
|
||||
floatVal, err := strconv.ParseFloat(itemStr, elemType.Bits())
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法转换为浮点型: %w", err)
|
||||
}
|
||||
elemValue.SetFloat(num)
|
||||
elemValue.SetFloat(floatVal)
|
||||
case reflect.Bool:
|
||||
if b, ok := item.(bool); ok {
|
||||
elemValue.SetBool(b)
|
||||
} else {
|
||||
return fmt.Errorf("无法转换为布尔型")
|
||||
itemStr := convertToString(item)
|
||||
b, err := strconv.ParseBool(itemStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法转换为布尔型: %w", err)
|
||||
}
|
||||
elemValue.SetBool(b)
|
||||
}
|
||||
case reflect.Struct:
|
||||
if isBasicStructType(elemType) {
|
||||
|
||||
Reference in New Issue
Block a user