Go 使用反射从深度嵌套的结构中提取标记

Go 使用反射从深度嵌套的结构中提取标记,go,struct,reflection,tags,Go,Struct,Reflection,Tags,我试图从一些深度嵌套的结构中提取一些标记。结构是从protobuf消息生成的,包含一个json标记 我有一个指向一个结构的指针,该结构可能包含一个包含我可能需要其标记的字段的结构。我可以使用类型进行迭代以获取结构的字段,但是当我遇到一个字段是指针时,如何获取它的值然后递归 // Struct has hierarchy like this a := &somepb.UpdateRequest{ Updates: []*somepb.UpdateRequest_Foo{

我试图从一些深度嵌套的结构中提取一些标记。结构是从protobuf消息生成的,包含一个json标记

我有一个指向一个结构的指针,该结构可能包含一个包含我可能需要其标记的字段的结构。我可以使用类型进行迭代以获取结构的字段,但是当我遇到一个字段是指针时,如何获取它的值然后递归

// Struct has hierarchy like this 
a := &somepb.UpdateRequest{
        Updates: []*somepb.UpdateRequest_Foo{
            &somepb.UpdateRequest_Foo{
                Id: 1,
                Foo: &somepb.FooInfo{
                    Metadata: &somepb.Metadata{
                        Name:        "Foo",
                        Description: "Bar",
                    },
                    Some:    "s",
                    Things:  "o",
                    Are:     "m",
                    Broken:  "e",
                    Here:    "p",
                },
            },
        },
 } 

// ParseStruct parses struct tags of given object
func ParseStruct(obj interface{}, tag string) {
    r := reflect.ValueOf(obj)
    if r.Kind() == reflect.Ptr {
        obj = r.Elem().Interface()
    }
    rv := reflect.TypeOf(obj)
    for i := 0; i < rv.NumField(); i++ {
        f := rv.Field(i)
        // This is to avoid getting tags for Metadata itself (from the struct above)
        // which is *Metadata, I want the tags for Name and Description
        // inside *Metadata instead
        if f.Type.Kind() == reflect.Ptr {
            value := f.Tag.Get(tag)
            if len(value) == 0 {
                continue
            }
            fmt.Println(value)
        }
    }
} 
//结构的层次结构如下
a:=&somepb.UpdateRequest{
更新:[]*somepb.UpdateRequest\u Foo{
&somepb.UpdateRequest\u Foo{
Id:1,
Foo:&somepb.FooInfo{
元数据:&somepb.Metadata{
姓名:"富",,
说明:“酒吧”,
},
有些人说:“s”,
事物:“o”,
是:"m",,
破碎:“e”,
这里是:“p”,
},
},
},
} 
//ParseStruct解析给定对象的结构标记
func ParseStruct(对象接口{},标记字符串){
r:=反射值(obj)
如果r.Kind()==reflect.Ptr{
obj=r.Elem().Interface()
}
rv:=反射类型(obj)
对于i:=0;i
两个
reflect.Type
reflect.Value
都有一个
Elem()
方法。根据,

元素
类型的方法

// Elem returns a type's element type.
// It panics if the type's Kind is not Array, Chan, Map, Ptr, or Slice.
Elem() Type
Value.Elem

func (v Value) Elem() Value

Elem returns the value that the interface v contains or that the pointer v points to. It panics if v's Kind is not Interface or Ptr. It returns the zero Value if v is nil. 
您可以使用
Elem()
方法获取指针的内容,然后使用内容进行递归。但是,如果您的原始API是
func(接口{},字符串)
,则需要使用
Value.Elem().interface()
来获得有意义的
接口{}
。但与此相反,我建议您将API更改为接受
reflect.Type
——因为这对于标记提取最为明确

示例代码:

func ParseStruct(t reflect.Type, tag string) {
    if t.Kind() == reflect.Ptr {
        t = t.Elm()
    }
    if t.Kind() != reflect.Struct {
        return
    }

    for i := 0; i < t.NumField(); i++ {
        f := t.Field(i)
        value := f.Tag.Get(tag)
        ft := t.Type
        if ft.Kind() == reflect.Ptr {
            ft = ft.Elem()
        }

        // It seems that you don't want a tag from an struct field; only other fields' tags are needed
        if ft.Kind() != reflect.Struct {
            if len(value) != 0 {
                fmt.Println(value)
            }
            continue
        }

        ParseStruct(ft,tag)
    }
}
func ParseStruct(t reflect.Type,标记字符串){
如果t.Kind()==reflect.Ptr{
t=t.Elm()
}
如果t.Kind()!=reflect.Struct{
返回
}
对于i:=0;i
请注意,此代码非常简单-它不处理切片或映射中的结构标记

当我遇到一个字段是指针时,我如何得到它的值和 然后再发生

// Struct has hierarchy like this 
a := &somepb.UpdateRequest{
        Updates: []*somepb.UpdateRequest_Foo{
            &somepb.UpdateRequest_Foo{
                Id: 1,
                Foo: &somepb.FooInfo{
                    Metadata: &somepb.Metadata{
                        Name:        "Foo",
                        Description: "Bar",
                    },
                    Some:    "s",
                    Things:  "o",
                    Are:     "m",
                    Broken:  "e",
                    Here:    "p",
                },
            },
        },
 } 

// ParseStruct parses struct tags of given object
func ParseStruct(obj interface{}, tag string) {
    r := reflect.ValueOf(obj)
    if r.Kind() == reflect.Ptr {
        obj = r.Elem().Interface()
    }
    rv := reflect.TypeOf(obj)
    for i := 0; i < rv.NumField(); i++ {
        f := rv.Field(i)
        // This is to avoid getting tags for Metadata itself (from the struct above)
        // which is *Metadata, I want the tags for Name and Description
        // inside *Metadata instead
        if f.Type.Kind() == reflect.Ptr {
            value := f.Tag.Get(tag)
            if len(value) == 0 {
                continue
            }
            fmt.Println(value)
        }
    }
} 
当您在
reflect.Value
reflect.Type
上遇到指针时,必须使用
Elem()
继续

reflect.ValueOf(new(string)).Elem()从值
*string
string


reflect.TypeOf(new(string)).Elem()从类型
*string
变为
string

。第12行应该是
ft.Type
,第14行应该是
ft=ft.Elem()
,第25行应该是
ft.Kind()
,否则你是对的。