Files
go-easy-utils/jsonx/Document.md
2025-07-08 15:26:54 +08:00

4.5 KiB
Raw Blame History

JsonToStruct 开发文档

处理指针类型

v := reflect.ValueOf(i)
if v.Kind() == reflect.Ptr {
    if v.IsNil() {
        return 0, nil // 检查解引用后的值是否为 nil
    }
    v = v.Elem() // 获取指针所指向的值
}

reflect包中,指针类型和其指向的值的类型是不同的。例如,一个指向int类型的指针,其类型是*int,而其指向的值的类型是int。因此,在使用reflect包时,我们需要注意区分指针类型和其指向的值的类型,否则可能会出现类型错误等问题。

因此,对于value为指针类型的情况我们需要通过Elem方法获取其指向的值然后再进行类型转换。而在使用Elem方法之前我们需要先检查v是否为指针类型因此使用循环语句进行判断。

举个例子

type Person struct {
    Name string
    Age  int
}

func main() {
    p := &Person{"Tom", 18}
    v := reflect.ValueOf(p).Elem()
    name := v.FieldByName("Name")
    age := v.FieldByName("Age")
    
    fmt.Println(name.String()) // 输出Tom
    fmt.Println(age.Int())     // 输出18
    
    name.SetString("Jerry")    // 修改 Name 字段的值
    age.SetInt(20)             // 修改 Age 字段的值
    
    fmt.Println(p.Name)        // 输出Jerry
    fmt.Println(p.Age)         // 输出20
}

在上面的例子中,我们首先将结构体指针 p 转换为 reflect.Value 类型的值 v,然后使用 v.Elem() 获取指针所指向的值,接着使用 FieldByName() 方法获取结构体字段的 reflect.Value 类型的值,最后使用 SetString()SetInt() 方法修改结构体字段的值。

处理Slice数据类型

case reflect.Slice:
    if subData, ok := value.([]interface{}); ok {
        if err := parseSlice(fieldValue, subData); err != nil {
            return err
        }
    } else {
        return fmt.Errorf("unexpected value type for slice: %T", value)
    }

将每个子元素都转换为结构体定义的数据类型再赋值给对应的字段。如果子元素是一个切片类型,需要进行递归处理。

首先获取到切片的元素类型 subType如果 subType 是一个指针类型,需要将其转换为实际的元素类型。然后创建一个与原始切片相同长度的新切片 subSliceValue并循环遍历原始切片中的每个子元素。在每个子元素中需要将其转换为结构体定义的数据类型可以通过递归调用 parseStruct 或 parseSlice 来实现。最后,将新的子元素设置到新切片 subSliceValue 中,并将其赋值给对应的字段。注意,如果元素类型是一个结构体,需要使用 Set 方法设置到新切片中,如果是基本类型,则需要判定类型然后添加到值中。

处理Map数据类型

第一种更加简洁的调用方法

case reflect.Map:
    if subData, ok := value.(map[string]interface{}); ok {
        subResult := reflect.New(fieldType.Type).Elem()
        if err := parseMap(subResult, subData); err != nil {
            return err
        }
        fieldValue.Set(subResult)
    }

其中 subResult 使用 reflect.New 创建一个新的结构体对象,然后使用 Elem() 方法获取其指针所指向的值,最后传递给 parseMap 方法进行解析。这样可以避免对 reflect.MakeMap 的使用,更加简洁。

第二种实在原有的代码进行更改。

case reflect.Map:
    if subData, ok := value.(map[string]interface{}); ok {
        subResult := reflect.MakeMap(fieldValue.Type())
        for subKey, subValue := range subData {
            subElem := reflect.New(fieldType.Type.Elem())
            err := parseValue(subElem.Elem(), subValue)
            if err != nil {
                return err
            }
            subResult.SetMapIndex(reflect.ValueOf(subKey), subElem.Elem())
        }
        fieldValue.Set(subResult)
    }

其中subData 是一个 map[string]interface{} 类型的变量,表示一个嵌套的 JSON 对象subResult 是一个 reflect.Value 类型的变量,表示一个 map 类型的变量的反射值subKey 和 subValue 分别表示 subData 中的键和值,可以通过 range 循环遍历 subData。

在调用 parseValue 函数时,需要传入 subElem.Elem() 作为第一个参数,这是因为 subElem 表示一个指向 fieldType.Type.Elem() 类型变量的指针,而 parseValue 函数需要传入一个值类型的变量。所以需要通过 subElem.Elem() 获取到 fieldType.Type.Elem() 类型的变量。