如何使用反射在Go中递归解析嵌套结构体?

7

我有一个嵌套的三层结构。

我想使用Go中的反射来解析它(使用递归函数)。使用反射和递归函数的原因是:

  • 可以有各种各样的字段(但前两个字段是固定的)
  • 字段类型不固定。
  • 嵌套层数的数量可能不同(在这个例子中只有三层。它可以更多)

以下是一些代码。

type Edge struct{
    Uid string `json:"uid,omitempty"`
    Name string `json:"name,omitempty"` 
    Read Article `json:"visited,omitempty"` 
} 
type Article struct {
    Uid string`json:"uid,omitempty"` 
    Namestring`json:"name,omitempty"`
    From Site `json:"from,omitempty"`
}
type Site struct{
    Uid string `json:"uid,omitempty"`
    Name string `json:"name,omitempty"` 
}
func CheckNestedStruct(edges interface{}){ 
    rv := reflect.ValueOf(edges).Elem() 
    uidField := rv.FieldByName("Uid")
    uid := getStructField(edges, "Name") // get value of Name from database 
    if (uid != ""){
        uidField.SetString(uid)
    }
    for i := 0 ; i < rv.NumField() ; i++ {
        field := rv.Field(i)
        fieldType := field.Kind()
        if (fieldType == reflect.Struct){
            CheckNestedStruct(field)
        }
    }
}
func main(){
    ....
    var edges Edges{
    ...
    ...
    }
    CheckNestedStruct(&edges)
}

当我运行这段代码时,在第一层中我得到了“type: *entity.SacWebIS”。然而,在第二次迭代/递归中,我得到了“type: *reflect.rtype”。 我还尝试了field.Interface()。 如何修改这段代码? 谢谢。 更新 解决方案是:
CheckNestedStruct(dg, field.Addr().Interface())

上传了纯代码。 - Luffy Cyliu
谢谢大家,我找到了答案。应该是 **CheckNestedStruct(field.Addr().Interface())**。 - Luffy Cyliu
3个回答

8

您正在对reflect.Value进行reflect.ValueOf调用,这将为您提供类型*reflect.rtype。如果您想将reflect.Value传回同一函数,则需要先调用Interface()

CheckNestedStruct(field.Interface())

无论您正在操作指针还是值,都会调用 Elem。如果您希望有条件地间接引用一个值,请使用 reflect.Indirect
rv := reflect.Indirect(reflect.ValueOf(edges))

嗨,谢谢你的回答。我找到了答案。你的答案接近正确,但应该是field.Addr().Interface()。 - Luffy Cyliu
1
@LuffyCyliu:这取决于你想要完成什么。如果使用“Indirect”,则不需要Addr,并且如果遇到不可寻址的字段,则会触发panic。 - JimB

2

如果要解析未知的JSON数据,而不知道字段的值和类型,您需要创建一个递归函数,该函数将深入解析嵌套的底层值。您可以使用类型断言来获取最终值。

func main() {
    m, ok := myJson.(map[string]interface{})
    newM := iterate(m)
    jsonBytes, err := json.Marshal(newM)
    if err != nil {
            fmt.Println(err)
    }
    fmt.Println(string(jsonBytes))
}

就将json转换为接口而言,它主要分为两种类型的嵌套结构,即接口切片[]interface{}或接口映射map[string]interface{},直到我们获取最终嵌套结构的值,我们可以使用Interface()来获取。因此,我们可以为深度嵌套结构创建递归。

func iterate(data interface{}) interface{} {

    if reflect.ValueOf(data).Kind() == reflect.Slice {
            d := reflect.ValueOf(data)
            tmpData := make([]interface{}, d.Len())
            returnSlice := make([]interface{}, d.Len())
            for i := 0; i < d.Len(); i++ {
                    tmpData[i] = d.Index(i).Interface()
            }
            for i, v := range tmpData {
                    returnSlice[i] = iterate(v)
            }
            return returnSlice
    } else if reflect.ValueOf(data).Kind() == reflect.Map {
            d := reflect.ValueOf(data)
            tmpData := make(map[string]interface{})
            for _, k := range d.MapKeys() {
                    typeOfValue := reflect.TypeOf(d.MapIndex(k).Interface()).Kind()
                    if typeOfValue == reflect.Map || typeOfValue == reflect.Slice {
                            tmpData[k.String()] = iterate(d.MapIndex(k).Interface())
                    } else {
                            tmpData[k.String()] = d.MapIndex(k).Interface()
                    }
            }
            return tmpData
    }
    return data
}

最后,对于一个接口{}的底层值,它将是原始类型的stringfloat64bool
func identify(output map[string]interface{}) {
    fmt.Printf("%T", output)
    for a, b := range output {
        switch bb := b.(type) {
        case string:
            fmt.Println("This is a string")
        case float64:
            fmt.Println("this is a float")
        case bool:
            fmt.Println("this is a boolean")
        case []interface{}:
        // Access the values in the JSON object and place them in an Item
        for _, itemValue := range jsonObj {
            fmt.Printf("%v is an interface\n", itemValue)
            identify(itemValue.(map[string]interface{}))
        }
        default:
            return
        }
    }
}

请在Go Playground上查看。


0

以上代码的简化版本(迭代函数):


func iterate(data interface{}) interface{} {
    d := reflect.ValueOf(data)
    if reflect.ValueOf(data).Kind() == reflect.Slice {
        returnSlice := make([]interface{}, d.Len())
        for i := 0; i < d.Len(); i++ {
            returnSlice[i] = iterate(d.Index(i).Interface())
        }
        return returnSlice
    } else if reflect.ValueOf(data).Kind() == reflect.Map {
        tmpData := make(map[string]interface{})
        for _, k := range d.MapKeys() {
            tmpData[k.String()] = iterate(d.MapIndex(k).Interface())
        }
        return tmpData
    } else {
        return data
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接