feat: 增强类型转换能力和代码重构
核心改进: 1. 新增 normalizeInputValue 函数 - 统一处理输入值的规范化,支持 R 接口、[]R 切片、reflect.Value - 避免重复封装,提升性能和类型安全性 2. 重构 getValueByPath 为独立函数 - 从 rfx 方法提取为独立函数,提高代码复用性 - 更好的职责分离,便于维护和测试 3. 显著增强 setValue 方法的类型转换能力 - 支持切片类型转换:[]any -> []T,自动转换每个元素 - 支持结构体类型转换:map -> struct 或 struct -> struct,按字段名匹配 - 保持指针切片的引用语义,避免不必要的对象复制 4. 新增 tryMapField 函数 - 支持 Map 键名的大小写不敏感访问 - 首字母大写的键会自动尝试小写版本(如 "Host" -> "host") 5. 新增 lowercaseFirst 辅助函数 - 用于首字母小写转换,配合 Map 键名查找 6. 更新测试用例 - 新增指针切片的使用示例 - 展示 []any 包含指针元素的场景 7. 文档全面更新 - 新增"高级类型转换"章节,详细说明切片、结构体、指针切片等转换 - 更新特性列表,突出增强的类型转换能力和 R 接口集成 - 补充 Map 大小写不敏感访问的说明 影响范围: - reflux.go: 使用 normalizeInputValue 统一输入处理 - rfx.go: 重构并增强 setValue、getValueByPath 等核心方法 - util.go: 新增多个辅助函数,代码行数增加 160 行 - rfx_example_test.go: 新增指针切片测试用例 向后兼容:完全兼容现有 API,仅增强内部实现和类型转换能力
This commit is contained in:
140
rfx.go
140
rfx.go
@@ -20,12 +20,12 @@ type rfx struct {
|
||||
|
||||
// Get 通过路径获取嵌套字段的值
|
||||
func (r *rfx) Get(p ...string) R {
|
||||
return &rfx{value: r.getValueByPath(p...)}
|
||||
return &rfx{value: getValueByPath(r.value, p...)}
|
||||
}
|
||||
|
||||
// Scope 创建一个指定路径的作用域视图(深度克隆)
|
||||
func (r *rfx) Scope(p ...string) R {
|
||||
v := r.getValueByPath(p...)
|
||||
v := getValueByPath(r.value, p...)
|
||||
if !v.IsValid() {
|
||||
return &rfx{value: reflect.Value{}}
|
||||
}
|
||||
@@ -35,56 +35,6 @@ func (r *rfx) Scope(p ...string) R {
|
||||
return &rfx{value: cloned}
|
||||
}
|
||||
|
||||
// getValueByPath 通过路径获取值的辅助方法
|
||||
// 支持两种路径格式:
|
||||
// 1. 多个参数: Get("Address", "City")
|
||||
// 2. 点号分割: Get("Address.City") 或混合使用 Get("Address.City", "ZipCode")
|
||||
func (r *rfx) getValueByPath(p ...string) reflect.Value {
|
||||
v := r.value
|
||||
for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
|
||||
if v.IsNil() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
// 展开所有路径片段,支持点号分割
|
||||
keys := expandPath(p...)
|
||||
|
||||
for _, key := range keys {
|
||||
if !v.IsValid() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Struct:
|
||||
v = tryStructField(v, key)
|
||||
case reflect.Map:
|
||||
v = v.MapIndex(reflect.ValueOf(key))
|
||||
case reflect.Slice, reflect.Array:
|
||||
// 尝试将 key 转换为索引
|
||||
idx, err := strconv.Atoi(key)
|
||||
if err == nil && idx >= 0 && idx < v.Len() {
|
||||
v = v.Index(idx)
|
||||
} else {
|
||||
return reflect.Value{}
|
||||
}
|
||||
default:
|
||||
return reflect.Value{}
|
||||
}
|
||||
|
||||
// 解引用指针和接口
|
||||
for v.IsValid() && (v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface) {
|
||||
if v.IsNil() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
v = v.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Set 设置指定路径的值,支持链式调用
|
||||
// 如果路径不存在或设置失败,会 panic
|
||||
func (r *rfx) Set(key string, v any) R {
|
||||
@@ -235,7 +185,7 @@ func (r *rfx) getParentValue(p ...string) reflect.Value {
|
||||
}
|
||||
return v
|
||||
}
|
||||
return r.getValueByPath(p...)
|
||||
return getValueByPath(r.value, p...)
|
||||
}
|
||||
|
||||
// setFieldValue 设置字段值的辅助方法
|
||||
@@ -315,24 +265,40 @@ func (r *rfx) setFieldValue(target reflect.Value, key string, v any) bool {
|
||||
// setValue 设置值的辅助方法
|
||||
// 使用 cast 库进行智能类型转换,支持更多的转换场景
|
||||
func (r *rfx) setValue(field reflect.Value, v any) bool {
|
||||
val := reflect.ValueOf(v)
|
||||
val, _, err := normalizeInputValue(v)
|
||||
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if !val.IsValid() {
|
||||
return false
|
||||
}
|
||||
|
||||
// 统一解开最外层的 interface 包装,便于后续根据底层实际类型做处理
|
||||
for val.Kind() == reflect.Interface && !val.IsNil() {
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
targetType := field.Type()
|
||||
|
||||
// 尝试直接赋值(类型完全匹配)
|
||||
if val.Type().AssignableTo(field.Type()) {
|
||||
if val.Type().AssignableTo(targetType) {
|
||||
field.Set(val)
|
||||
return true
|
||||
}
|
||||
|
||||
// 处理指针类型
|
||||
if field.Type().Kind() == reflect.Ptr {
|
||||
switch targetType.Kind() {
|
||||
case reflect.Ptr:
|
||||
// 处理指针类型
|
||||
// 如果传入的值已经是指针类型,尝试直接赋值
|
||||
if val.Type().AssignableTo(field.Type()) {
|
||||
if val.Type().AssignableTo(targetType) {
|
||||
field.Set(val)
|
||||
return true
|
||||
}
|
||||
|
||||
// 如果传入的值不是指针,创建新指针并设置值
|
||||
elemType := field.Type().Elem()
|
||||
elemType := targetType.Elem()
|
||||
newPtr := reflect.New(elemType)
|
||||
|
||||
// 递归设置指针指向的值
|
||||
@@ -342,13 +308,56 @@ func (r *rfx) setValue(field reflect.Value, v any) bool {
|
||||
|
||||
field.Set(newPtr)
|
||||
return true
|
||||
|
||||
case reflect.Slice: // 处理切片类型,支持从通用切片(如 []any)转换
|
||||
if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
|
||||
return false
|
||||
}
|
||||
|
||||
newSlice := reflect.MakeSlice(targetType, val.Len(), val.Len())
|
||||
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
if !r.setValue(newSlice.Index(i), val.Index(i)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
field.Set(newSlice)
|
||||
|
||||
return true
|
||||
|
||||
case reflect.Struct:
|
||||
// 遍历目标结构体的字段,从源值(结构体或 map)中按字段名取值并设置
|
||||
// 仅支持从 struct 或 map 填充
|
||||
if val.Kind() != reflect.Struct && val.Kind() != reflect.Map {
|
||||
return false
|
||||
}
|
||||
|
||||
fieldType := field.Type()
|
||||
for i := 0; i < fieldType.NumField(); i++ {
|
||||
dstField := field.Field(i)
|
||||
if !dstField.CanSet() {
|
||||
continue
|
||||
}
|
||||
|
||||
valField := getValueByPath(val, fieldType.Field(i).Name)
|
||||
|
||||
if !valField.IsValid() {
|
||||
continue
|
||||
}
|
||||
|
||||
// 使用 setValue 复用现有的类型转换逻辑,忽略单个字段失败
|
||||
if !r.setValue(dstField, valField) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
// 优先使用 cast 进行智能类型转换
|
||||
// 这样可以处理 string <-> number, number <-> bool 等常见转换
|
||||
targetType := field.Type()
|
||||
var converted any
|
||||
var err error
|
||||
|
||||
switch targetType.Kind() {
|
||||
case reflect.Bool:
|
||||
@@ -380,11 +389,6 @@ func (r *rfx) setValue(field reflect.Value, v any) bool {
|
||||
case reflect.String:
|
||||
converted, err = cast.ToStringE(v)
|
||||
default:
|
||||
// 对于 cast 不支持的类型,尝试标准的反射类型转换
|
||||
if val.Type().ConvertibleTo(field.Type()) {
|
||||
field.Set(val.Convert(field.Type()))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -398,6 +402,7 @@ func (r *rfx) setValue(field reflect.Value, v any) bool {
|
||||
}
|
||||
|
||||
field.Set(reflect.ValueOf(converted))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -453,8 +458,7 @@ func (r *rfx) Delete(p ...string) R {
|
||||
|
||||
// Exists 检查指定路径的值是否存在
|
||||
func (r *rfx) Exists(p ...string) bool {
|
||||
v := r.getValueByPath(p...)
|
||||
return v.IsValid()
|
||||
return getValueByPath(r.value, p...).IsValid()
|
||||
}
|
||||
|
||||
// Array 将当前值转换为 R 切片
|
||||
|
||||
Reference in New Issue
Block a user