递归遍历嵌套的结构体。

huangapple go评论99阅读模式
英文:

Recursively walk through nested structs

问题

我想构建一个方法,该方法以interface{}形式接受一个结构体,并在所提供的结构体的任何字段为nil时返回true

以下是我目前的代码:

// ContainsNil 如果所提供的结构体中的任何字段为nil,则返回true。
//
// 如果提供的对象不是结构体,则该方法将引发panic。
// 嵌套结构体将递归检查。
// 不会深度检查映射和切片。这可能会改变。
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) { // 导出字段的检查必须首先进行评估,以避免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"。

我已经进行了大量关于在结构体上使用反射的研究,但我还没有完全理解。有什么想法吗?

参考资料:

英文:

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 &lt; 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] &gt;= 65 == true &amp;&amp; field.Name[0] &lt;= 90 == true
}

And a failing test:

func Test_ContainsNil_NilNestedValue_ReturnsTrue(t *testing.T) {
	someNestedStruct := &amp;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

得分: 2

你检查当前字段是否为struct __value__,但你从未考虑它是指向结构体或其他类型的reflect.Ptr的情况,所以你的函数在这种情况下不会递归。以下是你的函数缺失的部分。

// ContainsNil函数会检查提供的结构体中是否有任何字段为nil。
//
// 如果提供的对象不是结构体,该方法将会引发panic。
// 嵌套结构体会被递归检查。
// 目前不会深度检查映射和切片,这可能会改变。
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) { // 导出字段检查必须首先进行评估,以避免panic。
            if field.Kind() == reflect.Ptr { // 指针或结构体指针的情况
                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
}

你可以在这里找到带有缺失部分的函数:https://play.golang.org/p/FdLxeee9UU

英文:

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 &lt; 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&#39;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
}

huangapple
  • 本文由 发表于 2017年8月23日 08:51:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/45828996.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定