完善数据类型转换

This commit is contained in:
Yun
2026-05-27 22:02:17 +08:00
parent 3fb090a620
commit 614ac34883
7 changed files with 1352 additions and 104 deletions
+53 -15
View File
@@ -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)
}
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()
+4 -3
View File
@@ -6,9 +6,9 @@ import (
// ChangeInfo 变更信息
type ChangeInfo struct {
Old string `json:"old"`
New string `json:"new"`
Val any `json:"val"`
Old any `json:"old"`
New any `json:"new"`
Val any `json:"val"`
}
// FieldInfo 字段信息
@@ -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
View File
@@ -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{
+36 -19
View File
@@ -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])
}
// 处理指针
for field.Kind() == reflect.Ptr {
if field.IsNil() {
field.Set(reflect.New(field.Type().Elem()))
}
field = field.Elem()
}
if len(index) > 1 {
// 只有中间节点才自动解引用指针
for field.Kind() == reflect.Ptr {
if field.IsNil() {
field.Set(reflect.New(field.Type().Elem()))
}
field = field.Elem()
}
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
View File
File diff suppressed because it is too large Load Diff
+30 -14
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"reflect"
"strings"
"time"
)
// 工具函数
@@ -39,9 +40,21 @@ func hasUnmarshalText(t reflect.Type) bool {
// 设置json.Unmarshaler接口的值
func setUnmarshalJSONValue(field reflect.Value, value interface{}) error {
jsonBytes, err := json.Marshal(value)
if err != nil {
return err
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
@@ -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
View File
@@ -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) {