递归地遍历嵌套结构体

3

我希望构建一个方法,该方法以结构体的interface{}形式接收输入,并在提供的结构体字段中有任何一个为nil时返回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)
}

测试代码执行时没有出现panic,但失败了,因为该方法返回的是false而不是true
我遇到的问题是,我无法弄清楚如何正确地将嵌套结构传递回递归调用ContainsNil
当对嵌套结构进行递归调用时,fieldIsExported方法返回false,因为它未收到我期望的值。
我期望fieldIsExported在第一次调用时接收到"SomeStruct",在第二次(递归)调用时接收到"SomeString"。第一个调用按预期进行,但在第二个调用中,fieldIsExported接收到"typ",而我希望它接收到"SomeString"。
我已经做了大量关于在结构体上使用反射的研究,但我还没有能够理解这个问题。有什么想法吗?
参考资料:
1个回答

2
您需要检查当前字段是否为结构体,但您从未考虑它是指向结构体或其他内容的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
}

这非常有道理。非常感谢您的帮助! - Joe

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