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 7-1 jmu-java-m02-使用二维数组存储多元线性方程组
  • ¥20 Qcustomplot缩小曲线形状问题
  • ¥15 企业资源规划ERP沙盘模拟
  • ¥15 树莓派控制机械臂传输命令报错,显示摄像头不存在
  • ¥15 前端echarts坐标轴问题
  • ¥15 CMFCPropertyPage
  • ¥15 ad5933的I2C
  • ¥15 请问RTX4060的笔记本电脑可以训练yolov5模型吗?
  • ¥15 数学建模求思路及代码
  • ¥50 silvaco GaN HEMT有栅极场板的击穿电压仿真问题