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