Reputation: 662
I am trying to extract some tags from some deeply nested structs. The structs were generated from a protobuf message and contain a json tag.
I have pointer to a struct that may contain a struct with fields whose tags that I may want. I can iterate using the type to get the fields of a struct but when I encounter a field that is pointer how do I get its value and then recurse?
// 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)
}
}
}
Upvotes: 0
Views: 1411
Reputation: 8232
Both reflect.Type
and reflect.Value
has an Elem()
methods. As per document,
Elem
method of Type
.
// 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.
You can use Elem()
method to get the content of the pointer and then use the content to recurse. However, given that your orignal API is func(interface{},string)
, you need to use Value.Elem().Interface()
to get a meaningful interface{}
. But instead of that, I suggest you change the API to accept reflect.Type
- for this is most clear for tag extraction.
Example code:
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)
}
}
Note this code is very simple - it does not process tags of structs in slices or maps.
Upvotes: 2
Reputation:
when I encounter a field that is pointer how do I get its value and then recurse?
when you encounter a pointer, over reflect.Value
or reflect.Type
, you got to take the Elem()
to proceed forward.
reflect.ValueOf(new(string)).Elem()
goes from the value *string
to string
reflect.TypeOf(new(string)).Elem()
goes from the type *string
to string
Upvotes: 0