Using reflect, how to set value to a struct field (pointer) - go

I try to set value to a struct field (pointer field) by reflect, but failed.
I get the name of a struct field, so use FieldByName to get the field
The field is a pointer.
I try to use FieldByName().Set FieldByName().SetPointer to set value.
type t struct {
b *int
}
func main() {
t := new(ts)
a := new(int)
ss := reflect.ValueOf(t).FieldByName("b").Set(a)
}
type t struct {
b *int
}
func main() {
t := new(ts)
a := new(int)
ss := reflect.ValueOf(t).FieldByName("b").SetPointer(a)
}
First code:
=======>
./test.go:14:50: cannot use a (type *int) as type reflect.Value in argument to reflect.ValueOf(t).FieldByName("b").Set
./test.go:14:50: reflect.ValueOf(t).FieldByName("b").Set(a) used as value
Second code:
=======>
./test.go:14:57: cannot use a (type *int) as type unsafe.Pointer in argument to reflect.ValueOf(t).FieldByName("b").SetPointer
./test.go:14:57: reflect.ValueOf(t).FieldByName("b").SetPointer(a) used as value
I want to use reflect to make the pointer field (name "b") alloced a space and set a value.

type ts struct {
B *int //field must be exported
}
func main() {
var t ts
foo := 5
a := &foo
//Use Elem() to indirect through the pointer and get struct field
//Use reflect.ValueOf(a) to satisfy Set() signature
reflect.ValueOf(&t).Elem().FieldByName("B").Set(reflect.ValueOf(a))
fmt.Println(*t.B)
}
Playground https://play.golang.org/p/gZt0ahTZMIi

Related

Is there a way to set a pointer struct field to a pointer pointing to the Zero value of that pointer type using reflect?

That was a mouthful of a title, let me explain more. Assuming I have a struct of all pointers (don't know of what type)
type A struct {
S *string
I *int
}
I want to write a function that takes a pointer to that struct and given a fieldName sets that field to a pointer to the Zero/empty value of that pointer. For example:
func setZeroForField(i any, fieldName string) {
// do stuff
}
a := A{}
setZeroForField(&a, "S")
setZeroForField(&a, "I")
// *a.S == ""
// *a.I == 0
Is there any way to do it using reflect? I know how to get the types of the fields of A but I can't use reflect.Indirect because it just returns a Zero value which in this case is a nil pointer, not the empty string or 0.
func setZeroForField(i any, fieldName string) {
rv := reflect.ValueOf(i).Elem()
fv := rv.FieldByName(fieldName)
fv.Set(reflect.New(fv.Type().Elem()))
}
https://go.dev/play/p/7clmztF5uaa

Get struct Tag with reflection - Error: type reflect.Value has no field or method Tag

Let's say we have this PoC:
package main
import (
"fmt"
"reflect"
"strings"
)
type MyStruct struct {
Str string `gorm:"size:10" json:"string"`
}
func main() {
aStruct := MyStruct{"Hello"}
s := reflect.ValueOf(&aStruct).Elem()
if s.Kind() == reflect.Struct {
for i := 0; i < s.NumField(); i++ {
field := s.Field(i)
if field.IsValid() {
if field.CanSet() {
fmt.Printf("%T\n", field) // reflect.Value, need reflect.StructField
gormTag := field.Tag.Get("gorm") // Compile ERROR HERE
gormSize := strings.Split(gormTag, "size:")[1]
fmt.Println(gormSize)
}
}
}
}
}
The error is:
go run test1.go
# command-line-arguments
./test1.go:22:22: field.Tag undefined (type reflect.Value has no field or method Tag)
Tested with go 1.14.6 and go 1.15.2.
In my understanding I need to cast (or get from) reflect.Value to reflect.StructField
Any ideas how to do that?
The tags belong to the type of fields of the struct, not to the values of the struct type.
reflect.ValueOf() returns you the wrapper of the value, not its type. To get the tag value, you need to start from the wrapper of the type, e.g. acquired with reflect.TypeOf():
t := reflect.TypeOf(&aStruct).Elem()
And the type wrapper of the field in the loop (which will be of type reflect.StructTag):
field := s.Field(i)
fieldt := t.Field(i)
And use fieldt to get the tag:
gormTag := fieldt.Tag.Get("gorm") // No error, this works
With this addition, output will be (try it on the Go Playground):
reflect.Value
10
See related: What are the use(s) for tags in Go?

panic: reflect: call of reflect.Value.FieldByName on interface Value

I have a variable of type interface{} and I want to change the value of a field using reflection. How can I do it? Variable must be of type interface{} due to other requirements. If the variable isn't of type interface{} all works, otherwise code throws
reflect: call of reflect.Value.FieldByName on interface Value
my code
package main
import (
"fmt"
"reflect"
)
func main() {
a := struct {
Name string
}{}
// works
reflect.ValueOf(&a).Elem().FieldByName("Name").SetString("Hello")
fmt.Printf("%#v\n", a)
var b interface{}
b = struct {
Name string
}{}
// panics
reflect.ValueOf(&b).Elem().FieldByName("Name").SetString("Hello")
fmt.Printf("%#v\n", b)
}
The application must call Elem() twice to get the struct value:
reflect.ValueOf(&b).Elem().Elem().FieldByName("Name").SetString("Hello")
The first call Elem() dereferences the pointer to interface{}. The second call to Elem() gets the value contained in the interface.
With this change, the panic is reflect.Value.SetString using unaddressable value.
The application cannot set fields directly on the struct value contained in the interface because values contained in an interface are not addressable.
Copy the struct value to a temporary variable, set the field in the temporary variable and copy the temporary variable back to the interface.
var b interface{}
b = struct {
Name string
}{}
// v is the interface{}
v := reflect.ValueOf(&b).Elem()
// Allocate a temporary variable with type of the struct.
// v.Elem() is the vale contained in the interface.
tmp := reflect.New(v.Elem().Type()).Elem()
// Copy the struct value contained in interface to
// the temporary variable.
tmp.Set(v.Elem())
// Set the field.
tmp.FieldByName("Name").SetString("Hello")
// Set the interface to the modified struct value.
v.Set(tmp)
fmt.Printf("%#v\n", b)
Run it on the Go playground.
The interface b is initialized using the value of the anonymous struct, so b contains a copy of the struct, and the values are not addressable. Initialize b using a pointer:
var b interface{}
b = &struct {
Name string
}{}
reflect.ValueOf(b).Elem().FieldByName("Name").SetString("Hello")

How to omit conditional field of struct within marshal

There is struct of MyStruct.
type MyStruct struct {
Code int `json:"Code"`
Flags uint8 `json:"Flags"`
OptionField int `json:",omitempty"`
}
Following code convert it to json.
f := MyStruct{Code:500, OptionField:41}
r, _ := json.Marshal(f)
fmt.Println(string(r)
I need to "OptionField" be optional. Some time it should exist in json with one of values [0, 1, 2, 3, ]. and in the other time it should exclude from json.
My problem is: omitempty will exclude it when the value is zero, and the default value of int is zero. Is there any way to omit field in condition (ex: omit if value is -1). Or there is any way to do it.
You could use *int instead of int and set the pointer value to nil in order to omit this.
package main
import (
"encoding/json"
"fmt"
)
type MyStruct struct {
Code int `json:"Code"`
Flags uint8 `json:"Flags"`
OptionField *int `json:",omitempty"`
}
func format(s MyStruct) string {
r, _ := json.Marshal(s)
return string(r)
}
func main() {
f := MyStruct{Code: 500, Flags: 10, OptionField: new(int)}
fmt.Println(format(f)) // {"Code":500,"Flags":10,"OptionField":0}
f.OptionField = nil
fmt.Println(format(f)) // {"Code":500,"Flags":10}
}

How to access unexported struct fields

Is there a way to use reflect to access unexported fields in Go 1.8?
This no longer seems to work: https://stackoverflow.com/a/17982725/555493
Note that reflect.DeepEqual works just fine (that is, it can access unexported fields) but I can't make heads or tails of that function. Here's a go playarea that shows it in action: https://play.golang.org/p/vyEvay6eVG. The src code is below
import (
"fmt"
"reflect"
)
type Foo struct {
private string
}
func main() {
x := Foo{"hello"}
y := Foo{"goodbye"}
z := Foo{"hello"}
fmt.Println(reflect.DeepEqual(x,y)) //false
fmt.Println(reflect.DeepEqual(x,z)) //true
}
If the struct is addressable, you can use unsafe.Pointer to access the field (read or write) it, like this:
rs := reflect.ValueOf(&MyStruct).Elem()
rf := rs.Field(n)
// rf can't be read or set.
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
// Now rf can be read and set.
See full example on the playground.
This use of unsafe.Pointer is valid according to the documentation and running go vet returns no errors.
If the struct is not addressable this trick won't work, but you can create an addressable copy like this:
rs = reflect.ValueOf(MyStruct)
rs2 := reflect.New(rs.Type()).Elem()
rs2.Set(rs)
rf = rs2.Field(0)
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
// Now rf can be read. Setting will succeed but only affects the temporary copy.
See full example on the playground.
Based on cpcallen's work:
import (
"reflect"
"unsafe"
)
func GetUnexportedField(field reflect.Value) interface{} {
return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Interface()
}
func SetUnexportedField(field reflect.Value, value interface{}) {
reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).
Elem().
Set(reflect.ValueOf(value))
}
reflect.NewAt might be confusing to read at first. It returns a reflect.Value representing a pointer to a value of the specified field.Type(), using unsafe.Pointer(field.UnsafeAddr()) as that pointer. In this context reflect.NewAt is different than reflect.New, which would return a pointer to a freshly initialized value.
Example:
type Foo struct {
unexportedField string
}
GetUnexportedField(reflect.ValueOf(&Foo{}).Elem().FieldByName("unexportedField"))
https://play.golang.org/p/IgjlQPYdKFR
reflect.DeepEqual() can do it because it has access to unexported features of the reflect package, in this case namely for the valueInterface() function, which takes a safe argument, which denies access to unexported field values via the Value.Interface() method if safe=true. reflect.DeepEqual() will (might) call that passing safe=false.
You can still do it, but you cannot use Value.Interface() for unexported fields. Instead you have to use type-specific methods, such as Value.String() for string, Value.Float() for floats, Value.Int() for ints etc. These will return you a copy of the value (which is enough to inspect it), but will not allow you to modify the field's value (which might be "partly" possible if Value.Interface() would work and the field type would be a pointer type).
If a field happens to be an interface type, you may use Value.Elem() to get to the value contained / wrapped by the interface value.
To demonstrate:
type Foo struct {
s string
i int
j interface{}
}
func main() {
x := Foo{"hello", 2, 3.0}
v := reflect.ValueOf(x)
s := v.FieldByName("s")
fmt.Printf("%T %v\n", s.String(), s.String())
i := v.FieldByName("i")
fmt.Printf("%T %v\n", i.Int(), i.Int())
j := v.FieldByName("j").Elem()
fmt.Printf("%T %v\n", j.Float(), j.Float())
}
Output (try it on the Go Playground):
string hello
int64 2
float64 3
package main
import (
"fmt"
"reflect"
"strings"
"unsafe"
)
type Person1 struct {
W3ID string
Name string
}
type Address1 struct {
city string
country string
}
type User1 struct {
name string
age int
address Address1
manager Person1
developer Person1
tech Person1
}
func showDetails(load, email interface{}) {
if reflect.ValueOf(load).Kind() == reflect.Struct {
typ := reflect.TypeOf(load)
value := reflect.ValueOf(load)
//#1 For struct, not addressable create a copy With Element.
value2 := reflect.New(value.Type()).Elem()
//#2 Value2 is addressable and can be set
value2.Set(value)
for i := 0; i < typ.NumField(); i++ {
if value.Field(i).Kind() == reflect.Struct {
rf := value2.Field(i)
/* #nosec G103 */
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
irf := rf.Interface()
typrf := reflect.TypeOf(irf)
nameP := typrf.String()
if strings.Contains(nameP, "Person") {
//fmt.Println(nameP, "FOUND !!!!!!! ")
for j := 0; j < typrf.NumField(); j++ {
re := rf.Field(j)
nameW := typrf.Field(j).Name
if strings.Contains(nameW, "W3ID") {
valueW := re.Interface()
fetchEmail := valueW.(string)
if fetchEmail == email {
fmt.Println(fetchEmail, " MATCH!!!!")
}
}
}
}
showDetails(irf, email)
} else {
// fmt.Printf("%d.Type:%T || Value:%#v\n",
// (i + 1), value.Field(i), value.Field(i))
}
}
}
}
func main() {
iD := "tsumi#in.org.com"
load := User1{
name: "John Doe",
age: 34,
address: Address1{
city: "New York",
country: "USA",
},
manager: Person1{
W3ID: "jBult#in.org.com",
Name: "Bualt",
},
developer: Person1{
W3ID: "tsumi#in.org.com",
Name: "Sumi",
},
tech: Person1{
W3ID: "lPaul#in.org.com",
Name: "Paul",
},
}
showDetails(load, iD)
}

Resources