Golang中如何通过反射访问子结构体

Golang中如何通过反射访问子结构体 我正在编写一个通用的JSON解析器,它有一些内置解析器无法满足的要求。我可以将JSON解析到一个映射中,然后将值分配给字段。但是,当我有一个结构体嵌套在另一个结构体中时,它就不再起作用了。

以下是一个不起作用的示例:

https://play.golang.org/p/UvSl3wl9U_p

注意 这是一个模块的通用方法。因此,在编译时我不知道被解析结构体的类型,所以我无法直接将其类型转换为childNode

2 回复

在第115行,您正在创建一个指向 reflect.Value 的指针的 reflect.Value

childV := reflect.ValueOf(&f)

相反,我建议您获取字段的地址,这样当您递归进入 writeValuesReflected 时,对 Elem() 的调用就能获取到正确的值:

childV := f.Addr()

更多关于Golang中如何通过反射访问子结构体的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中通过反射访问嵌套结构体字段时,需要递归遍历结构体。以下是针对您问题的解决方案:

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
)

type Parent struct {
    Name   string
    Child  Child
    Value  int
}

type Child struct {
    Name  string
    Age   int
}

func main() {
    jsonStr := `{"Name":"Parent1","Child":{"Name":"Child1","Age":10},"Value":100}`
    
    // 创建父结构体实例
    parent := &Parent{}
    
    // 解析JSON到map
    var data map[string]interface{}
    json.Unmarshal([]byte(jsonStr), &data)
    
    // 使用反射设置值
    setStructFields(parent, data)
    
    fmt.Printf("Parent: %+v\n", parent)
    fmt.Printf("Child Name: %s\n", parent.Child.Name)
    fmt.Printf("Child Age: %d\n", parent.Child.Age)
}

func setStructFields(obj interface{}, data map[string]interface{}) {
    v := reflect.ValueOf(obj).Elem()
    t := v.Type()
    
    for i := 0; i < v.NumField(); i++ {
        field := v.Field(i)
        fieldType := t.Field(i)
        fieldName := fieldType.Name
        
        // 检查map中是否存在该字段
        if value, exists := data[fieldName]; exists {
            // 处理嵌套结构体
            if field.Kind() == reflect.Struct {
                // 递归处理嵌套结构体
                if nestedMap, ok := value.(map[string]interface{}); ok {
                    // 创建嵌套结构体的指针
                    nestedPtr := reflect.New(field.Type())
                    setStructFields(nestedPtr.Interface(), nestedMap)
                    field.Set(nestedPtr.Elem())
                }
            } else {
                // 设置基本类型字段
                setFieldValue(field, value)
            }
        }
    }
}

func setFieldValue(field reflect.Value, value interface{}) {
    switch field.Kind() {
    case reflect.String:
        if str, ok := value.(string); ok {
            field.SetString(str)
        }
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        if num, ok := value.(float64); ok {
            field.SetInt(int64(num))
        }
    case reflect.Float32, reflect.Float64:
        if num, ok := value.(float64); ok {
            field.SetFloat(num)
        }
    case reflect.Bool:
        if b, ok := value.(bool); ok {
            field.SetBool(b)
        }
    }
}

对于更通用的解决方案,这里提供一个可以处理任意深度嵌套结构体的版本:

func setNestedField(obj interface{}, data map[string]interface{}) error {
    v := reflect.ValueOf(obj)
    if v.Kind() != reflect.Ptr || v.IsNil() {
        return fmt.Errorf("obj must be a non-nil pointer")
    }
    
    v = v.Elem()
    if v.Kind() != reflect.Struct {
        return fmt.Errorf("obj must point to a struct")
    }
    
    return setStructFieldsRecursive(v, data)
}

func setStructFieldsRecursive(v reflect.Value, data map[string]interface{}) error {
    t := v.Type()
    
    for i := 0; i < v.NumField(); i++ {
        field := v.Field(i)
        fieldType := t.Field(i)
        fieldName := fieldType.Name
        
        if value, exists := data[fieldName]; exists {
            // 处理嵌套结构体
            if field.Kind() == reflect.Struct {
                if nestedMap, ok := value.(map[string]interface{}); ok {
                    // 递归设置嵌套结构体字段
                    if err := setStructFieldsRecursive(field, nestedMap); err != nil {
                        return err
                    }
                }
            } else if field.Kind() == reflect.Ptr && field.Type().Elem().Kind() == reflect.Struct {
                // 处理指针类型的嵌套结构体
                if nestedMap, ok := value.(map[string]interface{}); ok {
                    // 创建新实例
                    nestedPtr := reflect.New(field.Type().Elem())
                    if err := setStructFieldsRecursive(nestedPtr.Elem(), nestedMap); err != nil {
                        return err
                    }
                    field.Set(nestedPtr)
                }
            } else {
                // 设置基本类型字段
                if err := setFieldWithTypeCheck(field, value); err != nil {
                    return err
                }
            }
        }
    }
    return nil
}

func setFieldWithTypeCheck(field reflect.Value, value interface{}) error {
    fieldType := field.Type()
    
    switch field.Kind() {
    case reflect.String:
        if str, ok := value.(string); ok {
            field.SetString(str)
            return nil
        }
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        switch v := value.(type) {
        case float64:
            field.SetInt(int64(v))
            return nil
        case int:
            field.SetInt(int64(v))
            return nil
        }
    case reflect.Float32, reflect.Float64:
        if num, ok := value.(float64); ok {
            field.SetFloat(num)
            return nil
        }
    case reflect.Bool:
        if b, ok := value.(bool); ok {
            field.SetBool(b)
            return nil
        }
    }
    
    return fmt.Errorf("type mismatch for field: expected %v, got %T", fieldType, value)
}

这个实现可以处理:

  1. 任意深度的嵌套结构体
  2. 结构体指针类型的嵌套字段
  3. 类型安全检查
  4. 递归遍历所有嵌套层级

使用示例:

type GrandChild struct {
    Name string
}

type Child struct {
    Name      string
    GrandChild GrandChild
}

type Parent struct {
    Name  string
    Child *Child
}

func main() {
    jsonStr := `{"Name":"Parent1","Child":{"Name":"Child1","GrandChild":{"Name":"GrandChild1"}}}`
    
    parent := &Parent{}
    var data map[string]interface{}
    json.Unmarshal([]byte(jsonStr), &data)
    
    if err := setNestedField(parent, data); err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    
    fmt.Printf("Parent: %+v\n", parent)
    fmt.Printf("Child: %+v\n", parent.Child)
    fmt.Printf("GrandChild: %+v\n", parent.Child.GrandChild)
}
回到顶部