【问题标题】:Recursively walk through nested structs递归遍历嵌套结构
【发布时间】:2017-08-23 00:51:00
【问题描述】:

我想构建一个将结构作为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 在第一次调用时收到“SomeStruct”,并在第二次(递归)调用时收到“SomeString”。第一次通话按预期进行,但在第二次通话中,fieldIsExported 收到“typ”,而我希望它收到“SomeString”。

我已经做了很多关于在结构上使用反射的研究,但我还没有搞清楚这个问题。想法?

参考资料:

【问题讨论】:

    标签: recursion go reflection


    【解决方案1】:

    您检查当前字段是否为结构 value,但您从不考虑将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
    }
    

    【讨论】:

    • 这很有意义。非常感谢您的帮助!
    猜你喜欢
    • 2011-09-08
    • 2021-09-19
    • 2021-12-24
    • 1970-01-01
    • 2012-02-29
    • 2017-08-25
    • 1970-01-01
    • 2020-07-20
    • 1970-01-01
    相关资源
    最近更新 更多