递归遍历嵌套结构

时间:2017-08-23 00:51:00

标签: recursion go reflection

我想构建一个方法,将结构作为interface{},如果提供的任何结构字段为零,则返回true

这就是我现在所拥有的:

// ContainsNil returns true if any fields within the supplied structure are nil.
//
// If the supplied object is not a struct, the method will panic.
// Nested structs are inspected recursively.
// Maps and slices are not inspected deeply. This may change.
func ContainsNil(obj interface{}) bool {
    if obj == nil {
        return true
    }
    s := reflect.Indirect(reflect.ValueOf(obj))
    for i := 0; i < s.NumField(); i++ {
        f := s.Type().Field(i)
        field := s.Field(i)
        if fieldIsExported(f) { // Exported-check must be evaluated first to avoid panic.
            if field.Kind() == reflect.Struct {
                if ContainsNil(field.Addr()) {
                    return true
                }
            } else {
                if field.IsNil() {
                    return true
                }
                if field.Interface() == nil {
                    return true
                }
            }
        }
    }
    return false
}

func fieldIsExported(field reflect.StructField) bool {
    log.Println(field.Name)
    return field.Name[0] >= 65 == true && field.Name[0] <= 90 == true
}

失败的测试:

func Test_ContainsNil_NilNestedValue_ReturnsTrue(t *testing.T) {
    someNestedStruct := &c.SomeNestedStruct{
        SomeStruct: c.SomeStruct{
            SomeString: nil,
        },
    }
    result := util.ContainsNil(someNestedStruct)
    assert.True(t, result)
}

测试代码执行时没有恐慌,但因为该方法返回false而不是true而失败。

我遇到的问题是,我无法弄清楚如何正确地将嵌套结构传递回ContainsNil的递归调用。

当对嵌套结构进行递归调用时,fieldIsExported方法返回false,因为它没有收到我期望它接收的值。

我希望fieldIsExported能够收到&#34; SomeStruct&#34;在第一次通话时,接收&#34; SomeString&#34;在第二个(递归)调用。第一个呼叫按预期进行,但在第二个呼叫时,fieldIsExported收到&#34; typ&#34;,当我希望它接收&#34; SomeString&#34;。

我已经做过一系列关于使用反射结构的研究,但我还没有能够理解这一点。想法?

参考文献:

1 个答案:

答案 0 :(得分:2)

您检查当前字段是否为struct ,但是当它是结构的reflect.Ptr或其他内容时,您从不考虑这种情况,因此您的函数永远不会因为那种情况。这是你缺少作品的功能。

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

// ContainsNil returns true if any fields within the supplied structure are nil.
//
// If the supplied object is not a struct, the method will panic.
// Nested structs are inspected recursively.
// Maps and slices are not inspected deeply. This may change.
func ContainsNil(obj interface{}) bool {
    if obj == nil {
        return true
    }
    s := reflect.Indirect(reflect.ValueOf(obj))
    for i := 0; i < s.NumField(); i++ {
        f := s.Type().Field(i)
        field := s.Field(i)
        if fieldIsExported(f) { // Exported-check must be evaluated first to avoid panic.
            if field.Kind() == reflect.Ptr { // case when it's a pointer or struct pointer
                if field.IsNil() {
                    return true
                }
                if ContainsNil(field.Interface()) {
                    return true
                }
            }
            if field.Kind() == reflect.Struct {
                if ContainsNil(field.Addr()) {
                    return true
                }
            } else {
                if field.IsNil() {
                    return true
                }
                if field.Interface() == nil {
                    return true
                }
            }
        }
    }
    return false
}