处理数组结构体

This commit is contained in:
Yun
2025-09-20 21:17:17 +08:00
parent 0b7a1cf46d
commit 02152b44bf
2 changed files with 283 additions and 136 deletions
+158 -12
View File
@@ -35,9 +35,9 @@ var typeConverters = map[reflect.Kind]converterFunc{
reflect.Float32: convertFloat[float32], reflect.Float32: convertFloat[float32],
reflect.Float64: convertFloat[float64], reflect.Float64: convertFloat[float64],
reflect.String: convertString, reflect.String: convertString,
// reflect.Slice: convertSlice, reflect.Slice: convertSlice,
// reflect.Array: convertArray, reflect.Array: convertArray,
// reflect.Map: convertMap, reflect.Map: convertMap,
} }
// AttactToStructAny 将 map[string]interface{} 类型的值附加到结构体中 // AttactToStructAny 将 map[string]interface{} 类型的值附加到结构体中
@@ -67,11 +67,13 @@ func AttactToStruct(structxx interface{}, updateMap map[string]string) (map[stri
// 获取结构体类型信息 // 获取结构体类型信息
t := v.Type() t := v.Type()
fieldMap := buildFieldMap(t) fieldMap := buildFieldMap(t)
fmt.Printf("字段映射2: %+v %+v\n", fieldMap,fieldMap["data"])
for mapKey, mapValue := range updateMap { for mapKey, mapValue := range updateMap {
fieldInfo, exists := fieldMap[mapKey] fieldInfo, exists := fieldMap[mapKey]
fmt.Printf("处理字段: %s, 信息: %+v\n", mapKey, fieldInfo)
if !exists { if !exists {
continue // 忽略不存在的字段 return nil, fmt.Errorf("字段 %s 不存在", mapKey)
} }
// 安全获取字段值,处理嵌套指针 // 安全获取字段值,处理嵌套指针
@@ -87,14 +89,22 @@ func AttactToStruct(structxx interface{}, updateMap map[string]string) (map[stri
// 处理指针类型 // 处理指针类型
if fieldInfo.IsPtr { if fieldInfo.IsPtr {
if field.IsNil() { if field.IsNil() {
// 创建新的指针实例
newValue := reflect.New(fieldInfo.FieldType.Elem()) newValue := reflect.New(fieldInfo.FieldType.Elem())
field.Set(newValue) field.Set(newValue)
} }
field = field.Elem() field = field.Elem()
} }
// 处理嵌套结构体(包括指针解引用后的结构体) // 处理切片和数组类型
if fieldInfo.IsSlice || fieldInfo.IsArray {
err := processSliceOrArrayField(field, mapValue, mapKey)
if err != nil {
return nil, fmt.Errorf("处理切片/数组字段 %s 失败: %w", mapKey, err)
}
continue
}
// 处理嵌套结构体
if field.Kind() == reflect.Struct && !isBasicStructType(field.Type()) { if field.Kind() == reflect.Struct && !isBasicStructType(field.Type()) {
nestedChanges, err := processNestedStruct(field, mapValue, mapKey) nestedChanges, err := processNestedStruct(field, mapValue, mapKey)
if err != nil { if err != nil {
@@ -136,6 +146,122 @@ func AttactToStruct(structxx interface{}, updateMap map[string]string) (map[stri
return changeMap, nil return changeMap, nil
} }
// 处理切片和数组字段
func processSliceOrArrayField(field reflect.Value, value string, fieldKey string) error {
// 尝试解析JSON数组
var jsonData []interface{}
if err := json.Unmarshal([]byte(value), &jsonData); err != nil {
return fmt.Errorf("字段 %s 的值必须是JSON数组格式: %w", fieldKey, err)
}
fieldType := field.Type()
elemType := fieldType.Elem()
// 创建新的切片或数组
var newContainer reflect.Value
if fieldType.Kind() == reflect.Slice {
// 切片:动态长度
newContainer = reflect.MakeSlice(fieldType, len(jsonData), len(jsonData))
} else if fieldType.Kind() == reflect.Array {
// 数组:固定长度
if len(jsonData) != fieldType.Len() {
return fmt.Errorf("字段 %s 的数组长度不匹配: 期望 %d, 实际 %d",
fieldKey, fieldType.Len(), len(jsonData))
}
newContainer = reflect.New(fieldType).Elem()
} else {
return fmt.Errorf("字段 %s 不是切片或数组类型", fieldKey)
}
// 填充数据
for i, item := range jsonData {
elemValue := newContainer.Index(i)
err := setSliceElementValue(elemValue, item, elemType)
if err != nil {
return fmt.Errorf("设置切片/数组元素失败: %w", err)
}
}
field.Set(newContainer)
return nil
}
// 设置切片/数组元素的值
func setSliceElementValue(elemValue reflect.Value, item interface{}, elemType reflect.Type) error {
// 处理指针类型的元素
if elemType.Kind() == reflect.Ptr {
if elemValue.IsNil() {
elemValue.Set(reflect.New(elemType.Elem()))
}
return setSliceElementValue(elemValue.Elem(), item, elemType.Elem())
}
// 根据元素类型进行转换
switch elemType.Kind() {
case reflect.String:
if str, ok := item.(string); ok {
elemValue.SetString(str)
} else {
elemValue.SetString(fmt.Sprintf("%v", item))
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if num, ok := item.(float64); ok { // JSON数字默认是float64
elemValue.SetInt(int64(num))
} else {
return fmt.Errorf("无法将 %v 转换为整型", item)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if num, ok := item.(float64); ok {
elemValue.SetUint(uint64(num))
} else {
return fmt.Errorf("无法将 %v 转换为无符号整型", item)
}
case reflect.Float32, reflect.Float64:
if num, ok := item.(float64); ok {
elemValue.SetFloat(num)
} else {
return fmt.Errorf("无法将 %v 转换为浮点型", item)
}
case reflect.Bool:
if b, ok := item.(bool); ok {
elemValue.SetBool(b)
} else {
return fmt.Errorf("无法将 %v 转换为布尔型", item)
}
case reflect.Struct:
// 处理结构体元素
if isBasicStructType(elemType) {
return setBasicStructElement(elemValue, item, elemType)
}
// 对于自定义结构体,需要JSON unmarshal
jsonBytes, err := json.Marshal(item)
if err != nil {
return err
}
return json.Unmarshal(jsonBytes, elemValue.Addr().Interface())
default:
return fmt.Errorf("不支持的切片元素类型: %s", elemType.Kind())
}
return nil
}
// 设置基本结构体元素(如time.Time)
func setBasicStructElement(elemValue reflect.Value, item interface{}, elemType reflect.Type) error {
jsonBytes, err := json.Marshal(item)
if err != nil {
return err
}
return json.Unmarshal(jsonBytes, elemValue.Addr().Interface())
}
// getFieldByIndexSafe 安全地通过索引路径获取字段,处理嵌套指针 // getFieldByIndexSafe 安全地通过索引路径获取字段,处理嵌套指针
func getFieldByIndexSafe(v reflect.Value, index []int) (reflect.Value, error) { func getFieldByIndexSafe(v reflect.Value, index []int) (reflect.Value, error) {
if len(index) == 0 { if len(index) == 0 {
@@ -173,17 +299,21 @@ type fieldInfo struct {
Name string Name string
IsPtr bool IsPtr bool
FieldType reflect.Type FieldType reflect.Type
IsSlice bool // 新增:标识是否为切片或数组
IsArray bool // 新增:标识是否为数组
} }
func buildFieldMap(t reflect.Type) map[string]fieldInfo { func buildFieldMap(t reflect.Type) map[string]fieldInfo {
fieldMap := make(map[string]fieldInfo) fieldMap := make(map[string]fieldInfo)
buildFieldMapRecursive(t, []int{}, fieldMap, "") buildFieldMapRecursive(t, []int{}, fieldMap, "")
fmt.Printf("字段映射表: %+v\n", fieldMap)
return fieldMap return fieldMap
} }
func buildFieldMapRecursive(t reflect.Type, index []int, fieldMap map[string]fieldInfo, prefix string) { func buildFieldMapRecursive(t reflect.Type, index []int, fieldMap map[string]fieldInfo, prefix string) {
for i := 0; i < t.NumField(); i++ { for i := 0; i < t.NumField(); i++ {
field := t.Field(i) field := t.Field(i)
fmt.Printf("处理字段 索引: %+v %+v\n", field, field.Type.Kind())
if !field.IsExported() { if !field.IsExported() {
continue continue
} }
@@ -205,12 +335,22 @@ func buildFieldMapRecursive(t reflect.Type, index []int, fieldMap map[string]fie
actualType = fieldType.Elem() actualType = fieldType.Elem()
} }
// 处理切片和数组类型 - 不进行递归展开
if actualType.Kind() == reflect.Slice || actualType.Kind() == reflect.Array {
fieldMap[fullKey] = fieldInfo{
Index: currentIndex,
Name: field.Name,
IsPtr: isPtr,
FieldType: fieldType,
IsSlice: true,
}
continue
}
// 如果是结构体且不是基本类型,递归处理 // 如果是结构体且不是基本类型,递归处理
if actualType.Kind() == reflect.Struct && !isBasicStructType(actualType) { if actualType.Kind() == reflect.Struct && !isBasicStructType(actualType) {
// 对于指针或非指针的结构体都进行递归展开
buildFieldMapRecursive(actualType, currentIndex, fieldMap, fullKey) buildFieldMapRecursive(actualType, currentIndex, fieldMap, fullKey)
// 同时记录当前字段的信息
fieldMap[fullKey] = fieldInfo{ fieldMap[fullKey] = fieldInfo{
Index: currentIndex, Index: currentIndex,
Name: field.Name, Name: field.Name,
@@ -401,14 +541,20 @@ func convertString(field reflect.Value, value string) (interface{}, error) {
func convertSlice(field reflect.Value, value string) (interface{}, error) { func convertSlice(field reflect.Value, value string) (interface{}, error) {
// 实现切片转换逻辑 // 实现切片转换逻辑
// elemType := field.Type().Elem() var result []interface{}
// 根据元素类型进行解析 if err := json.Unmarshal([]byte(value), &result); err != nil {
return nil, fmt.Errorf("切片转换未实现") return nil, err
}
return result, nil
} }
func convertArray(field reflect.Value, value string) (interface{}, error) { func convertArray(field reflect.Value, value string) (interface{}, error) {
// 实现数组转换逻辑 // 实现数组转换逻辑
return nil, fmt.Errorf("数组转换未实现") 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) { func convertMap(field reflect.Value, value string) (interface{}, error) {
+5 -4
View File
@@ -463,10 +463,10 @@ func TestAttactToStruct_ErrorScenarios(t *testing.T) {
expectedErr: "需要是非空指针", expectedErr: "需要是非空指针",
}, },
{ {
name: "不支持的类型", name: "自定义类型",
structPtr: &struct{ Data []string }{}, structPtr: &struct{ Data []string }{},
input: map[string]string{"data": "test"}, input: map[string]string{"Data": `["test","test2"]`},
expectedErr: "不支持的类型", expectedErr: "",
}, },
{ {
name: "无效的嵌套路径", name: "无效的嵌套路径",
@@ -475,7 +475,7 @@ func TestAttactToStruct_ErrorScenarios(t *testing.T) {
"nonexistent.field": "value", "nonexistent.field": "value",
"basic.name": "test", "basic.name": "test",
}, },
expectedErr: "", // 应该忽略不存在的字段而不报错 expectedErr: "字段 nonexistent.field 不存在", // 应该忽略不存在的字段而不报错
}, },
} }
@@ -592,6 +592,7 @@ func BenchmarkAttactToStruct_Nested(b *testing.B) {
"basic.salary": "50000.0", "basic.salary": "50000.0",
"basic.is_active": "true", "basic.is_active": "true",
"comment": "test", "comment": "test",
"amount": "500.0",
} }
b.ResetTimer() b.ResetTimer()