Files
structx/field_mapper.go
2025-09-21 16:30:45 +08:00

89 lines
2.0 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()
fieldMap := make(map[string]FieldInfo)
dm.buildFieldMapRecursive(t, []int{}, fieldMap, "")
cacheMutex.Lock()
typeInfoCache[t] = fieldMap
cacheMutex.Unlock()
return fieldMap
}
// 递归构建字段映射表
func (dm *defaultFieldMapper) buildFieldMapRecursive(t reflect.Type, index []int, fieldMap map[string]FieldInfo, prefix string) {
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 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.buildFieldMapRecursive(actualType, currentIndex, fieldMap, fullKey)
}
fieldMap[fullKey] = FieldInfo{
Index: currentIndex,
Name: field.Name,
IsPtr: isPtr,
FieldType: fieldType,
}
}
}