diff --git a/rfx.go b/rfx.go index 4ee22af..dccd0ec 100644 --- a/rfx.go +++ b/rfx.go @@ -272,99 +272,109 @@ func (r *rfx) setValue(field reflect.Value, v any) bool { 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(targetType) { - field.Set(val) - return true - } - - // 如果源值是指针但目标不是指针,尝试解引用后再赋值 - if val.Kind() == reflect.Ptr && !val.IsNil() && targetType.Kind() != reflect.Ptr { - derefVal := val.Elem() - if derefVal.Type().AssignableTo(targetType) { - field.Set(derefVal) + if !val.IsValid() { + // 如果值无效(通常是 nil 的情况) + // 对于可以为 nil 的类型,直接设置零值 + switch targetType.Kind() { + case reflect.Ptr, reflect.Interface, reflect.Slice, reflect.Map, reflect.Chan, reflect.Func: + field.Set(reflect.Zero(targetType)) return true + default: + // 对于基础类型,继续使用后面的 cast 转换逻辑 + // 不提前返回 false + } + } else { + // 如果 val 有效,进行正常的类型处理 + // 统一解开最外层的 interface 包装,便于后续根据底层实际类型做处理 + for val.Kind() == reflect.Interface && !val.IsNil() { + val = val.Elem() } - // 解引用后继续使用下面的逻辑处理 - val = derefVal - } - switch targetType.Kind() { - case reflect.Ptr: - // 处理指针类型 - // 如果传入的值已经是指针类型,尝试直接赋值 + // 尝试直接赋值(类型完全匹配) if val.Type().AssignableTo(targetType) { field.Set(val) return true } - // 如果传入的值不是指针,创建新指针并设置值 - elemType := targetType.Elem() - newPtr := reflect.New(elemType) - - // 递归设置指针指向的值 - if !r.setValue(newPtr.Elem(), v) { - return false + // 如果源值是指针但目标不是指针,尝试解引用后再赋值 + if val.Kind() == reflect.Ptr && !val.IsNil() && targetType.Kind() != reflect.Ptr { + derefVal := val.Elem() + if derefVal.Type().AssignableTo(targetType) { + field.Set(derefVal) + return true + } + // 解引用后继续使用下面的逻辑处理 + val = derefVal } - field.Set(newPtr) - return true + switch targetType.Kind() { + case reflect.Ptr: + // 处理指针类型 + // 如果传入的值已经是指针类型,尝试直接赋值 + if val.Type().AssignableTo(targetType) { + field.Set(val) + return true + } - case reflect.Slice: // 处理切片类型,支持从通用切片(如 []any)转换 - if val.Kind() != reflect.Slice && val.Kind() != reflect.Array { - return false - } + // 如果传入的值不是指针,创建新指针并设置值 + elemType := targetType.Elem() + newPtr := reflect.New(elemType) - newSlice := reflect.MakeSlice(targetType, val.Len(), val.Len()) - - for i := 0; i < val.Len(); i++ { - if !r.setValue(newSlice.Index(i), val.Index(i)) { + // 递归设置指针指向的值 + if !r.setValue(newPtr.Elem(), v) { return false } - } - field.Set(newSlice) + field.Set(newPtr) + return true - 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) { + case reflect.Slice: // 处理切片类型,支持从通用切片(如 []any)转换 + if val.Kind() != reflect.Slice && val.Kind() != reflect.Array { return false } - } - return true + 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 进行智能类型转换 @@ -406,7 +416,7 @@ func (r *rfx) setValue(field reflect.Value, v any) bool { if err != nil { // 如果 cast 失败,尝试标准的反射类型转换作为后备 - if val.Type().ConvertibleTo(field.Type()) { + if val.IsValid() && val.Type().ConvertibleTo(field.Type()) { field.Set(val.Convert(field.Type())) return true }