Files
structx/field_mapper.go
T
2026-05-27 22:02:17 +08:00

108 lines
2.7 KiB
Go

package structx
import (
"reflect"
"sync"
)
// FieldMapper 字段映射器接口
type FieldMapper interface {
GetFieldMap(t reflect.Type) map[string]FieldInfo
}
var (
// 全局缓存 避免重复计算 TODO: 考虑使用LRU缓存
typeInfoCache = make(map[reflect.Type]map[string]FieldInfo)
cacheMutex = &sync.RWMutex{}
)
// defaultFieldMapper 默认字段映射器
type defaultFieldMapper struct{}
func (dm *defaultFieldMapper) GetFieldMap(t reflect.Type) map[string]FieldInfo {
cacheMutex.RLock()
if cached, exists := typeInfoCache[t]; exists {
cacheMutex.RUnlock()
return cached
}
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, "")
typeInfoCache[t] = fieldMap
cacheMutex.Unlock()
return fieldMap
}
// 递归构建字段映射表
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() {
continue
}
currentIndex := append(index, i)
jsonTag := getJSONTagName(field)
fullKey := jsonTag
if prefix != "" {
fullKey = prefix + "." + jsonTag
}
fieldType := field.Type
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,
Name: field.Name,
IsPtr: isPtr,
FieldType: fieldType,
IsSlice: actualType.Kind() == reflect.Slice,
IsArray: actualType.Kind() == reflect.Array,
}
continue
}
if actualType.Kind() == reflect.Struct && !isBasicStructType(actualType) {
dm.buildFieldMapRecursiveWithVisited(actualType, currentIndex, fieldMap, fullKey, stack)
}
fieldMap[fullKey] = FieldInfo{
Index: currentIndex,
Name: field.Name,
IsPtr: isPtr,
FieldType: fieldType,
}
}
}