In Golang, for every "thing", the medatata describing it is type. But there is also kind which sometimes you need to consult to get the real underlying type (if the kind is a pointer).
To get a list of fields, you need to check if it is a struct or a pointer to a struct. Then, when getting a list of fields, you must check if a field is anonymous or not. That's why you need a recursion to get the field list, but in that recursion some field names can happen twice (because they are declared in different anonymous structs).
Then to get/set a field by name, you need to check if the original is a struct or a pointer to a struct (again). If it is a struct you get the field value with ...FieldByName(fieldName) if it is a pointer it's ...Elem().FieldByName(fieldName), but (only) for values you can use reflect.Indirect(). For every field you have two metadata types describing it: reflect.Value and reflect.StructField. In Java, when you have a reference to Field, you have everything you need to get/set/describe that field.
In short Java and Python reflection is much simpler than Golang reflection. At least, for me it is.
But, since sometimes I really had to do this reflection thing, I decided to simplify it as much as possible and created go-reflector. You basically give him any value, and it gives you a simple API to work with fields, methods and tags.
Example usage:
import "github.com/tkrajina/go-reflector/reflector"
p := Person{}
// Initializing:
obj := reflector.New(p)
// Fields:
obj.Field("Name").IsValid()
val, err := obj.Field("Name").Get()
err := obj.Field("Name").Set("Something")
// Tags:
jsonTag := obj.Field("Name").Tag("json")
jsonTag := obj.Field("Name").TagExpanded("json")
fieldTagsMap := obj.Field("Name").Tags()
// Listing fields:
fields := obj.FieldsAll()
fields := obj.FieldsFlattened()
fields := obj.Fields()
doubleDeclaredFields := obj.FindDoubleFields()
if len(doubleDeclaredFields) > 0 {
fmt.Println("Detected multiple fields with same name:", doubleDeclaredFields)
}
// Calling methods:
resp, err := obj.Method("Hi").Call("John", "Smith")
if err != nil {
fmt.Println("Cannot call method:", err.Error())
}
if resp.IsError() {
fmt.Println("Method returned an error:", resp.Error.Error())
} else {
fmt.Println("Method call response:", resp.Result)
}
// Get all methods:
for _, method := range obj.Methods() {
fmt.Println("Method", method.Name(), "with input types", method.InTypes(), "and output types", method.OutTypes())
}
Read the README here: https://github.com/tkrajina/go-reflector.
No comments:
Post a Comment