How do I change fields a slice of structs using reflect? - go

I have the following https://play.golang.org/p/TlHCX29QZr
package main
import (
"fmt"
"reflect"
)
type A struct {
Name string
Age int
}
func change(a interface{}) {
aa := reflect.Indirect(reflect.ValueOf(a))
for i := 0; i < aa.NumField(); i++ {
field := aa.Field(i)
switch field.Interface().(type) {
case string:
field.Set(reflect.ValueOf("fred"))
case int:
field.Set(reflect.ValueOf(54))
default:
fmt.Println("unknown field")
}
}
}
func main() {
a := &A{"bob", 120}
b := []*A{}
c := []struct {
Alias string
Months int
}{}
d := []struct {
First string
Years int
}{
{"james", 22},
{"ricky", 32},
{"bobby", 12},
{"rachel", 82},
}
change(a)
fmt.Println(a) // want &A{"fred", 54}
change(b)
fmt.Println(b) // want []*A{&A{"fred", 54}}
change(c)
fmt.Println(c) // want []struct{struct{"fred", 54}}
change(d)
fmt.Println(d) // want []struct{struct{"fred", 54}, struct{"fred", 54}, struct{"fred", 54}, struct{"fred", 54}}
}
As you can see, some of the variables are an empty slice and some are not. For those that are empty, I need to add 1 struct of {"fred", 54}. For those slices that are not empty I need to change all values to {"fred", 54}. I do not know in advance what the fields are...only that if there is a string field the value should be "fred" and if an int field 54.
I'm able to change the value of "a" but everything else fails with "panic: reflect: call of reflect.Value.NumField on slice Value". I'm not sure where to go on this. Thank you!

As stated in the comments, you cannot use NumField on a slice, since that method is allowed only for reflect.Values that are of kind reflect.Struct.
So if you want to handle both kinds you need to know which one was passed in.
if rv.Kind() == reflect.Struct {
changeStruct(rv)
}
if rv.Kind() == reflect.Slice {
changeSlice(rv)
}
Now, if you want to append to an empty slice, you either have to pass in a pointer to the slice or you have to return the new slice.
change(&b)
change(&c)
Also, to be able to initialize that single element that you want to append you first need to know its type, to get the type of a slice's element you first get the slice's reflect.Type and then use its Elem method to get the type of the slice's element. With that type you can then use reflect.New to allocate a new value of that type and append it to the slice.
var elem reflect.Value
// rv is the slice
typ := rv.Type().Elem()
if typ.Kind() == reflect.Ptr {
elem = reflect.New(typ.Elem())
}
if typ.Kind() == reflect.Struct {
elem = reflect.New(typ).Elem()
}
To then loop over a slice you can use the reflect.Value.Len and reflect.Value.Index methods.
ln := rv.Len()
for i := 0; i < ln; i++ {
changerv(rv.Index(i))
}
The code:
func change(a interface{}) {
rv := reflect.ValueOf(a)
changerv(rv)
}
func changerv(rv reflect.Value) {
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
if rv.Kind() == reflect.Struct {
changeStruct(rv)
}
if rv.Kind() == reflect.Slice {
changeSlice(rv)
}
}
// assumes rv is a slice
func changeSlice(rv reflect.Value) {
ln := rv.Len()
if ln == 0 && rv.CanAddr() {
var elem reflect.Value
typ := rv.Type().Elem()
if typ.Kind() == reflect.Ptr {
elem = reflect.New(typ.Elem())
}
if typ.Kind() == reflect.Struct {
elem = reflect.New(typ).Elem()
}
rv.Set(reflect.Append(rv, elem))
}
ln = rv.Len()
for i := 0; i < ln; i++ {
changerv(rv.Index(i))
}
}
// assumes rv is a struct
func changeStruct(rv reflect.Value) {
if !rv.CanAddr() {
return
}
for i := 0; i < rv.NumField(); i++ {
field := rv.Field(i)
switch field.Kind() {
case reflect.String:
field.SetString("fred")
case reflect.Int:
field.SetInt(54)
default:
fmt.Println("unknown field")
}
}
}
The playground.

Related

Golang reflect/iterate through interface{}

I’m looking to iterate through an interfaces keys.
Goal:
I want to implement a kind of middleware that checks for outgoing data (being marshalled to JSON) and edits nil slices to empty slices.
It should be agnostic/generic so that I don't need to specify field names. Ideally I can pass any struct as an interface and replace nil slices with empty slices.
Controller level
type Tag struct {
Name string
}
type BaseModel struct {
ID uuid.UUID
Active bool
}
type Model struct {
BaseModel // embedded struct
Name string
Number int
Tags []Tag
}
newModel, err := GetModel()
if err != nil {
...
}
RespondAsJson(w, newModel)
Middleware / Middle man json responder
It takes an interface to be generic/agnostic and reusable
in many different controllers
//(1) Attempting to use a map
func RespondWithJson(w http.ResponseWriter, data interface{}) {
obj, ok := data.(map[string]interface{})
// (1.1) OK == false
obj, ok := data.(map[interface{}]interface{})
// (1.2) OK == false
var newMap map[string]interface{}
bytes, _ := json.Marshal(&obj)
json.unMarshal(bytes, &newMap)
// (1.3) newMap has no underlying types on fields
// Nil slice of Tags went in, and it comes out as
// value=nil and type=interface{}
}
//(2) Skipping two as I believe this works, I'd like to avoid implementing it though.
//(3)
//(3.1)
RespondWithJson(w, newModel)
func RespondWithJson(w http.ResponseWriter, data interface{}) {
e := reflect.ValueOf(&data) // data = {*interface{} | Model}
if e.Kind() == reflect.Pointer {
e = e.Elem()
// e has a flag of 22, e.Elem() has a flag of 404
}
for i := 0; i < e.NumField(); i++ {
//PANIC: reflect: call of reflect.Value.NumField on interface Value
...
}
}
//(3.2)
// Reference: https://go.dev/blog/laws-of-reflection (third law)
RespondWithJson(w, newModel)
func RespondWithJson(w http.ResponseWriter, data interface{}) {
e := reflect.ValueOf(data) // data = {interface{} | Model}
if e.Kind() == reflect.Pointer {
e = e.Elem()
}
for i := 0; i < e.NumField(); i++ {
field := e.Field(i)
if field.Kind() == reflect.Slice && field.isNil() {
ok := field.CanSet() // OK == false
// Reference third law description in the reference above
valueOfField1 := reflect.ValueOf(&field)
ok := valueOfField1 .CanSet() // OK == false
...
valueOfField2 := reflect.ValueOf(field.Interface())
ok := valueOfField2.CanSet() // OK == false
...
}
}
}
//(3.3)
// Reference: (https://stackoverflow.com/questions/64211864/setting-nil-pointers-address-with-reflections) and others like it
RespondWithJson(w, newModel)
func RespondWithJson(w http.ResponseWriter, data interface{}) {
e := reflect.ValueOf(data) // {interface{} | Model}
if e.Kind() == reflect.Pointer { e = e.Elem() }
for i := 0; i < e.NumField(); i++ {
field := e.Field(i)
if field.Kind() == reflect.Slice && field.IsNil() {
tmp := reflect.New(field.Type())
if tmp.Kind() == reflect.Pointer { tmp = tmp.Elem()}
// (3.3.1)
ok := tmp.CanSet() // OK == true
tmp.Set(reflect.MakeSlice(field.Type(),0,0))
ok := field.CanSet()
// OK == false, tmp.set doesn't affect field value && can't set
field with value of tmp
// (3.3.2)
ok := tmp.Elem().CanSet()
// PANIC - call of reflect.value.Elem on Slicevalue
...
}
}
}
//(3.4)
// I can get it to work with passing &model to the function
// Once I'm inside the function, it's seen as an interface (or a
// *interface and the above is my results
RespondWithJson(w, &newModel)
func RespondWithJson(w http.ResponseWriter, data interface{}) {
e := reflect.ValueOf(data) // Data is {interface{} | *Model}
if e.Kind() == reflect.Pointer {
e = e.Elem()
// e has a flag of 22, e.Elem() has a flag of 409
}
for i := 0; i < e.NumField(); i++ {
field := e.Field(i)
if field.Kind() == reflect.Slice && field.IsNil() {
ok := field.CanSet()
// OK == true, field is addressable
if ok {
field.Set(reflect.MakeSlice(field.Type(), 0, 0))
// Success! Tags: nil turned into Tags: []
}
}
}
}
After that and many more.. random interations, I've found a way to make it work by passing memory address of struct to function which takes interface value.
If possible, I'd like to avoid the need to do this, as the function signature won't pick it up and it just leaves a small amount of room for error for other people on my team. I can of course just document the function, but its not bullet proof :)
Does anyone have suggestions for making this work without starting with a memory address to a struct? Can I set a field of an interface? ty very much!
In general, what you're probably looking for is something involving reflection. Your current code:
func someFunction(data interface{}) {
y := reflect.ValueOf(&data)
for i := 0; i < y.NumField(); i++ {
// PANIC: y is not a value of a struct
}
}
is pretty close, but it fails because data is a pointer. You can fix this by doing:
y := reflect.ValueOf(data)
if y.Kind() == reflect.Pointer {
y = y.Elem()
}
This will ensure that you have the actual value, and not a pointer to the value, allowing you to do NumField on it. Inside the loop, you check if the field is a slice and if it's nil and then set it to the value of a new instance of a slice of your field's type.
yField := y.Field(i)
if yField.Kind() == reflect.Slice && yField.IsNil() {
yField.Set(reflect.MakeSlice(yField.Elem().Type(), 0, 0)
}
Here we use Elem again because yField points to a slice, and so to create a new slice we need the inner type.
Finally, you need to add recursion to handle inner types if any of your fields are structs:
func SomeFunction(data interface{}) ([]byte, error) {
someFunctionInner(reflect.ValueOf(data))
return json.Marshal(data)
}
func someFunctionInner(v reflect.Value) {
if v.Kind() == reflect.Pointer {
v = v.Elem()
}
for i := 0; i < v.NumField(); i++ {
vField := v.Field(i)
switch vField.Kind() {
case reflect.Slice:
if vField.IsNil() {
vField.Set(reflect.MakeSlice(vField.Type(), 0, 0))
} else {
for j := 0; j < vField.Len(); j++ {
vFieldInner := vField.Index(j)
if vFieldInner.Kind() != reflect.Struct &&
(vFieldInner.Kind() != reflect.Pointer || vFieldInner.Elem().Kind() != reflect.Struct) {
continue
}
someFunctionInner(vFieldInner.Index(j))
}
}
case reflect.Pointer, reflect.Struct:
someFunctionInner(vField)
default:
}
}
}
and then you call it like this:
func main() {
m := Model{}
b, d := SomeFunction(&m)
fmt.Printf("Data: %+v\n", m)
fmt.Printf("JSON: %s, Error: %v\n", b, d)
}
Data: {BaseModel:{ID: Active:false} Name: Number:0 Tags:[]}
JSON: {"ID":"","Active":false,"Name":"","Number":0,"Tags":[]}, Error: <nil>
Note that I haven't added any sort of error-handling. Nor have I handled anything above regular pointers. Also, this function does expect a reference to an object because it is making modifications to said object. Finally, this code doesn't touch array logic at all. Still, this is likely what you're looking for.

Nested string fields can not be updated using reflection in an arbitrary Go struct

I'm trying to update all string fields in a struct and its subfields using reflection in golang for an arbitrary struct as follows:
package main
import (
"fmt"
"reflect"
"strings"
)
func main() {
type Inner struct {
In1 string
In2 []string
}
type Type struct {
Name string
Names []string
NewSt Inner
}
a := Type{
Name: " [ (Amir[ ",
Names: nil,
NewSt: Inner{
In1: " [in1",
In2: []string{" [in2( "},
},
}
trims(&a)
fmt.Printf("%#v\n", a)
}
func trim(str string) string {
return strings.TrimSpace(strings.Trim(str, "[](){}, "))
}
func trims(ps interface{}) {
v := reflect.ValueOf(ps).Elem() // Elem() dereferences pointer
for i := 0; i < v.NumField(); i++ {
fv := v.Field(i)
switch fv.Kind() {
case reflect.String:
fv.SetString(trim(fv.String()))
case reflect.Struct:
in := fv.Interface()
trims(&in)
}
}
}
But I get panic: reflect: call of reflect.Value.Elem on struct Value error.
How can I fix it or is there any better way that I can do such thing??
Thanks.
func trims(ps interface{}) {
v := reflect.ValueOf(ps)
if v.Kind() == reflect.Ptr {
v = v.Elem() // Elem() dereferences pointer
}
if v.Kind() != reflect.Struct {
panic("not struct")
}
for i := 0; i < v.NumField(); i++ {
fv := v.Field(i)
switch fv.Kind() {
case reflect.String:
fv.SetString(trim(fv.String()))
case reflect.Struct:
// use Addr() to get an addressable
// value of the field
in := fv.Addr().Interface()
// do not use &in, that evaluates
// to *interface{}, that's almost
// NEVER what you want
trims(in)
case reflect.Slice:
if fv.Type().Elem().Kind() == reflect.String {
for i := 0; i < fv.Len(); i++ {
fv.Index(i).SetString(trim(fv.Index(i).String()))
}
}
}
}
}
https://go.dev/play/p/JkJTJzTckNA

Reading nested structure using reflection

I write a recursive function that iterate over deep nested struct like the following:
type Container struct {
Name string
Items []Item
}
type Item struct {
Name string
Info Info
Vals []string
}
// recursively reads nested struct, prints string values
func ReadStruct(st interface{}) {
val := reflect.ValueOf(st).Elem()
for i := 0; i < val.NumField(); i++ {
fmt.Println(val.Type().Field(i).Type.Kind())
switch val.Type().Field(i).Type.Kind() {
case reflect.Struct:
ReadStruct(val.Field(i)) // panic: call of reflect.Value.Elem on struct Value
case reflect.Slice:
// How to iterate over the reflect.Slice?
case reflect.String:
fmt.Printf("%v=%v", val.Type().Field(i).Name, val.Field(i))
}
}
how to get access to inner objects (slices, structs) to work with them using reflect?
to iterate over slice i tried to use:
for i:= 0; i < val.Field(i).Slice(0, val.Field(i).Len()); i++ { //error: reflect.Value doesnt support indexing
//some work
}
Couple of errors in your code.
First, you only have to call Value.Elem() if the passed value is a pointer. When you iterate over the fields and you find a field of struct type, and you recursively call ReadStruct() with that, that won't be a pointer and thus you mustn't call Elem() on that.
So do it like this:
val := reflect.ValueOf(st)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
Next, since you start ReadStruct() by calling reflect.ValueOf(), that assumes you have to pass non-reflect values to ReadStruct() (that is, not values of type reflect.Value).
But when you iterate over the fields, calling Value.Field(), you get a reflect.Value wrapping the field. You have to call Value.Interface() to extract the non-reflect value form it, to be passed in recursive calls.
To iterate over slices, simply use Value.Len() to get the slice length, and Value.Index() to get the ith element of the slice.
Here's the corrected version of your traversal function:
// I used this type as you didn't post it in your question.
type Info struct {
Key, Value string
}
func ReadStruct(st interface{}) {
val := reflect.ValueOf(st)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
for i := 0; i < val.NumField(); i++ {
// fmt.Println(val.Type().Field(i).Type.Kind())
f := val.Field(i)
switch f.Kind() {
case reflect.Struct:
ReadStruct(f.Interface())
case reflect.Slice:
for j := 0; j < f.Len(); j++ {
ReadStruct(f.Index(i).Interface())
}
case reflect.String:
fmt.Printf("%v=%v\n", val.Type().Field(i).Name, val.Field(i).Interface())
}
}
}
Testing it:
c := &Container{
Name: "c1",
Items: []Item{
{
Name: "i1",
Info: Info{Key: "k1", Value: "v1"},
},
{
Name: "i2",
Info: Info{Key: "k2", Value: "v2"},
},
},
}
ReadStruct(c)
Output (try it on the Go Playground):
Name=c1
Name=i2
Key=k2
Value=v2
Name=i2
Key=k2
Value=v2
Note: By using recursive calls, you are extracting and re-acquiring reflect.Value values. It would be more efficient to always work with reflect.Values, so you can avoid these unnecessary calls.
This is how you could do that:
func ReadStruct(st interface{}) {
readStruct(reflect.ValueOf(st))
}
func readStruct(val reflect.Value) {
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
for i := 0; i < val.NumField(); i++ {
// fmt.Println(val.Type().Field(i).Type.Kind())
f := val.Field(i)
switch f.Kind() {
case reflect.Struct:
readStruct(f)
case reflect.Slice:
for j := 0; j < f.Len(); j++ {
readStruct(f.Index(i))
}
case reflect.String:
fmt.Printf("%v=%v\n", val.Type().Field(i).Name, val.Field(i))
}
}
}
This will output the same. Try this one on the Go Playground.

How to use reflection to assign values to unknown structures

Structs may contains float32, int32, string or pointer to struct.
Here is my code. But i do not know how to assign values to the structs inside.
type T struct {
A int
B string
C *P
}
type P struct {
A string
B int
}
func main() {
t := T{}
decode(&t, []string{"99", "abc", "abc", "99"})
fmt.Println(t)
}
You could do something like this:
func decode(a interface{}, val []string) {
rdecode(reflect.ValueOf(a).Elem(), val)
}
func rdecode(rv reflect.Value, val []string) int {
var index int
for i := 0; i < rv.NumField(); i++ {
f := rv.Field(i)
if f.Kind() == reflect.Ptr {
if f.IsNil() {
f.Set(reflect.New(f.Type().Elem()))
}
f = f.Elem()
}
switch f.Kind() {
case reflect.Int:
tmp, _ := strconv.Atoi(val[index])
f.Set(reflect.ValueOf(tmp))
index++
case reflect.String:
f.SetString(val[index])
index++
case reflect.Struct:
index += rdecode(f, val[index:])
default:
break
}
}
return index
}
https://play.golang.com/p/Jyj0flzDBhu

How do you set fields in a nested struct to their zero value?

Let's say I have an instance of struct Thing1 that I want to json.Marshal
type Thing1 struct {
A string `json:"a,omitempty"`
B int `json:"b,omitempty"`
C Thing2 `json:"c,omitempty"`
}
type Thing2 struct {
D bool `json:"d,omitempty"`
E int `json:"e,omitempty"`
}
...
thing1 := Thing1{
A: "test",
B: 42,
C: Thing2{D: true, E: 43},
}
How would you write a function that takes an instance of any struct and a list of fields to redact and returns a clone (or just mutates) of the incoming object, but with the redacted fields set to their zero values?
redact(thing1, []string{"B", "D"})
thing1 == Thing1{
A: "test",
B: 0,
C: Thing2{D: false, E: 43},
}
I can't use json:"-" as a field tag because the current ones in place are required for the query language I am using (Dgraph).
edit: not in the example, but objects inside arrays should also be redacted if applicable
Use reflect to manipulate the value of struct's field. Below is a proof of concept from what I have written in the comment. Since this is just a poc, you might need to adjust/modify the code to follow your needs.
This function mutate the original data. Code is self explanatory.
func redact(target interface{}, fieldsToModify []string) {
// if target is not pointer, then immediately return
// modifying struct's field requires addresable object
addrValue := reflect.ValueOf(target)
if addrValue.Kind() != reflect.Ptr {
return
}
// if target is not struct then immediatelly return
// this might need to be modified as per your needs
targetValue := addrValue.Elem()
targetType := targetValue.Type()
if targetType.Kind() != reflect.Struct {
return
}
// loop the fields
for i := 0; i < targetType.NumField(); i++ {
fType := targetType.Field(i)
fValue := targetValue.Field(i)
// if the field type is struct, then call redact() recursively
if fValue.Kind() == reflect.Struct {
redact(fValue.Addr().Interface(), fieldsToModify)
continue
}
// if the field is slice, loop then call redact() recursively
if fValue.Kind() == reflect.Array || fValue.Kind() == reflect.Slice {
for i := 0; i < fValue.Len(); i++ {
redact(fValue.Index(i).Addr().Interface(), fieldsToModify)
}
continue
}
// loop the fieldsToModify
for _, fieldToModify := range fieldsToModify {
if fieldToModify == fType.Name && fValue.CanSet() {
fValue.Set(reflect.Zero(fType.Type))
}
}
}
}
The redact() function pointer data in first parameter, since modifying fields require addresable object.
type Thing2 struct {
D bool `json:"d,omitempty"`
E int `json:"e,omitempty"`
}
type Thing1 struct {
A string `json:"a,omitempty"`
B int `json:"b,omitempty"`
C Thing2 `json:"c,omitempty"`
H []Thing2 `json:"h,omitempty"`
}
thing1 := Thing1{
A: "test",
B: 42,
C: Thing2{D: true, E: 43},
H: []Thing2{Thing2{D: true, E: 43}},
}
fmt.Printf("before: %#v \n", thing1)
// before: main.Thing1{A:"test", B:42, C:main.Thing2{D:true, E:43}, H:[]main.Thing2{main.Thing2{D:true, E:43}}}
redact(&thing1, []string{"B", "D"})
fmt.Printf("after: %#v \n", thing1)
// after: main.Thing1{A:"test", B:0, C:main.Thing2{D:false, E:43}, H:[]main.Thing2{main.Thing2{D:false, E:43}}}
Playground: https://play.golang.org/p/wy39DGdSVV7
Here's how to do it with the reflect package:
func redact(x interface{}, names []string) error {
// Starting value must be a pointer.
v := reflect.ValueOf(x)
if v.Kind() != reflect.Ptr {
return errors.New("not pointer")
}
// Create map for easy lookup.
m := make(map[string]bool)
for _, name := range names {
m[name] = true
}
redactValue(v, m)
return nil
}
func redactValue(v reflect.Value, names map[string]bool) {
switch v.Kind() {
case reflect.Ptr:
if v.IsZero() {
return
}
redactValue(v.Elem(), names)
case reflect.Interface:
if v.IsZero() {
return
}
iv := v.Elem()
switch iv.Kind() {
case reflect.Slice, reflect.Ptr:
redactValue(iv, names)
case reflect.Struct, reflect.Array:
// Copy required for modification.
copy := reflect.New(iv.Type()).Elem()
copy.Set(iv)
redactValue(copy, names)
v.Set(copy)
}
case reflect.Struct:
t := v.Type()
for i := 0; i < t.NumField(); i++ {
sf := t.Field(i)
ft := sf.Type
fv := v.Field(i)
if names[sf.Name] {
// Clobber the field.
fv.Set(reflect.Zero(ft))
continue
}
redactValue(fv, names)
}
case reflect.Slice, reflect.Array:
for i := 0; i < v.Len(); i++ {
redactValue(v.Index(i), names)
}
}
}
Run it on the playground.
This answer handles structs, slices, arrays, pointers and interfaces.

Resources