github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/reflector/README.md (about) 1 # Golang reflector 2 3 First, don't use reflection if you don't have to. 4 5 But if you really have to... This library offers a simplified Golang reflection abstraction. 6 7 ## Getting and setting fields 8 9 Let's suppose we have structs like: 10 11 type Address struct { 12 Street string `tag:"be" tag2:"1,2,3"` 13 Number int `tag:"bi"` 14 } 15 16 type Person struct { 17 Name string `tag:"bu"` 18 Address 19 } 20 21 func (p Person) Hi(name string) string { 22 return fmt.Sprintf("Hi %s my name is %s", name, p.Name) 23 } 24 25 Initialize the **reflector**'s object wrapper: 26 27 import "github.com/tkrajina/go-reflector/reflector" 28 29 p := Person{} 30 obj := reflector.New(p) 31 32 Check if a field is valid: 33 34 obj.Field("Name").IsValid() 35 36 Get field value: 37 38 val, err := obj.Field("Name").Get() 39 40 Set field value: 41 42 p := Person{} 43 obj := reflector.New(&p) 44 err := obj.Field("Name").Set("Something") 45 46 Don't forget to use a pointer in `New()`, otherwise setters won't work. Field "settability" can be checked by using `field.IsSettable()`. 47 48 ## Tags 49 50 Get a tag: 51 52 jsonTag := obj.Field("Name").Tag("json") 53 54 Get tag values array (exploded with "," as a delimiter): 55 56 jsonTag := obj.Field("Name").TagExpanded("json") 57 58 Or get a map with all field tags: 59 60 fieldTagsMap := obj.Field("Name").Tags() 61 62 ## Listing fields 63 64 There are three ways to list fields: 65 66 * List all fields: This will include anonymous structs **and** fields declared in anonymous structs (`Name`, `Address`, `Street`, `Number`). 67 * List flattened fields: Includes fields declared in anonymous structs **without** anonymous structs (`Name`, `Street`, `Number`). 68 * List nonflattened fields: Includes anonymous structs **without** their fields (`Name`, `Address`). This is the way fields are actually declared in the code. 69 70 Depending on which listing you want, you can use: 71 72 fields := obj.FieldsAll() 73 fields := obj.FieldsFlattened() 74 fields := obj.Fields() 75 76 You can only get the list of anonymous fields with `obj.FieldsAnonymous()`. 77 78 Be aware that because of anonymous structs, some field names can be returned twice! 79 In most cases this is not a desired situation, but you can use **reflector** to detect such situations in your code: 80 81 doubleDeclaredFields := obj.FindDoubleFields() 82 if len(doubleDeclaredFields) > 0 { 83 fmt.Println("Detected multiple fields with same name:", doubleDeclaredFields) 84 } 85 86 The field listing will contain both exported and unexported fields. Unexported fields are not gettable/settable, but their tags are readable. 87 88 ## Calling methods 89 90 obj := reflector.New(&Person{}) 91 resp, err := obj.Method("Hi").Call("John", "Smith") 92 93 The `err` is not `nil` only if something was wrong with the method (for example invalid method name, or wrong argument number/types), not with the actual method call. 94 If the call finished, `err` will be `nil`. 95 If the method call returned an error, you can check it with: 96 97 if resp.IsError() { 98 fmt.Println("Got an error:", resp.Error.Error()) 99 } else { 100 fmt.Println("Method call response:", resp.Result) 101 } 102 103 ## Listing methods 104 105 for _, method := range obj.Methods() { 106 fmt.Println("Method", method.Name(), "with input types", method.InTypes(), "and output types", method.OutTypes()) 107 } 108 109 ## Getting length, getting and setting slice/array/string/map elements 110 111 Map: 112 113 m := map[string]interface{}{"aaa", 17} 114 o := reflector.New(m) 115 fmt.Println("Length", o.Len()) 116 val, found := o.GetByKey("aaa") 117 o.SetByKey("bbb", "new value") 118 fmt.Println("keys:", o.Keys()) 119 120 Slice, string: 121 122 l := []int{1, 2, 3} 123 o := reflector.New(o) 124 fmt.Println("Length", o.Len()) 125 val, found := o.GetByIndex(0) 126 o.SetByIndex(0, 19) 127 128 ## Performance 129 130 When reflecting the same type multiple times, **reflector** will cache as much reflection metadata as possible **only once** and use that in future. 131 132 If you make any changes to the library, run `make test-performance` to check performance improvement/deterioration before/after your change. 133 134 $ make test-performance 135 N=1000000 go test -v ./... -run=TestPerformance 136 === RUN TestPerformance 137 WITH REFLECTION 138 n= 1000000 139 started: 2016-05-25 08:35:15.5258 140 ended: 2016-05-25 08:35:19.5258 141 duration: 4.269112s 142 --- PASS: TestPerformance (4.27s) 143 === RUN TestPerformancePlain 144 WITHOUT REFLECTION 145 n= 1000000 146 started: 2016-05-25 08:35:19.5258 147 ended: 2016-05-25 08:35:19.5258 148 duration: 0.005237s 149 --- PASS: TestPerformancePlain (0.01s) 150 PASS 151 ok github.com/tkrajina/go-reflector/reflector 4.285s 152 153 Keep those numbers in mind before deciding to use reflection :) 154 155 License 156 ------- 157 158 **Reflector** is licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)