dq62957 2017-08-23 00:51
浏览 69
已采纳

递归地遍历嵌套结构

I want to build a method that takes a struct as an interface{} and returns true if any of the supplied struct's fields are nil.

Here's what I have at the moment:

// 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
}

And a failing test:

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

The test code executes without panicking, but fails because the method returns false rather than true.

The issue I'm having is that I can't figure out how to properly pass the nested struct back into the recursive call to ContainsNil.

When recursive call is made for the nested structure, the fieldIsExported method returns false because it's not receiving the value that I would expect it to be receiving.

I expect fieldIsExported to receive "SomeStruct" on its first call, and receive "SomeString" on the second (recursive) call. The first call goes as expected, but on the second call, fieldIsExported receives "typ", when I would expect it to receive "SomeString".

I've done a bunch of research about using reflect on structs, but I haven't been able to get my head around this yet. Ideas?

References:

  • 写回答

1条回答 默认 最新

  • doushifen4060 2017-08-23 02:10
    关注

    You check if the current field is a struct value, but you never account for the case when it is a reflect.Ptr to a struct or something else, so your function never recurses for that case. Here is your function with the missing piece.

    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
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 运筹学排序问题中的在线排序
  • ¥15 关于docker部署flink集成hadoop的yarn,请教个问题 flink启动yarn-session.sh连不上hadoop,这个整了好几天一直不行,求帮忙看一下怎么解决
  • ¥30 求一段fortran代码用IVF编译运行的结果
  • ¥15 深度学习根据CNN网络模型,搭建BP模型并训练MNIST数据集
  • ¥15 lammps拉伸应力应变曲线分析
  • ¥15 C++ 头文件/宏冲突问题解决
  • ¥15 用comsol模拟大气湍流通过底部加热(温度不同)的腔体
  • ¥50 安卓adb backup备份子用户应用数据失败
  • ¥20 有人能用聚类分析帮我分析一下文本内容嘛
  • ¥15 请问Lammps做复合材料拉伸模拟,应力应变曲线问题