Note: I want to do the same as How to set a struct member that is a pointer to a string using reflection in Go , but in a more generic way. The existing question does not solve my problem.
I have structs with different kinds of fields that I want to populate using reflection:
type MyStruct struct {
SomeInt int
SomeString string
SomeIntPtr *int
SomeStringPtr *string
}
The value I want to write into the individual fields is retrieved from a configuration-store and parsed into the correct type, similar to this:
func getValueForField(fieldName string) interface{}
For int
and *int
types, the function returns an int
(wrapped in an interface). For string
and *string
, the function returns string
(behind an interface) and so on, for all types.
--> Note that it does NOT return *int
/ *string
!
And now I want to assign the value to the struct fields:
var field reflect.Value = reflect.ValueOf(ptrToMyStruct).Elem().Field(i)
var value interface{} = getValueForField(....)
var isPointer bool = field.Kind() == reflect.Ptr
// assign "value" to "field":
if isPointer {
// ??
field.Set(reflect.ValueOf(value).Addr()) // panic: reflect.Value.Addr of unaddressable value
} else {
field.Set(reflect.ValueOf(value)) // works
}
Assigning those values to concrete types is easy and works as expected. But I can't assign an int
type (returned from getValueForField
) to an *int
field without somehow getting an address. And since I only have an interface{}
, this needs to be done via reflection.
Here's a link to the Go Playground: https://play.golang.org/p/zElEGHgx1IO
Use reflect.New()
to construct a new pointer value for the field, set the pointed value from value
, and then set this new value to the pointer field.
For example:
type MyStruct struct {
SomeIntPtr *int
SomeStringPtr *string
}
var ms MyStruct
// Set int pointer
{
var i interface{} = 3 // of type int
f := reflect.ValueOf(&ms).Elem().FieldByName("SomeIntPtr")
x := reflect.New(f.Type().Elem())
x.Elem().Set(reflect.ValueOf(i))
f.Set(x)
}
// Set string pointer
{
var i interface{} = "hi" // of type string
f := reflect.ValueOf(&ms).Elem().FieldByName("SomeStringPtr")
x := reflect.New(f.Type().Elem())
x.Elem().Set(reflect.ValueOf(i))
f.Set(x)
}
fmt.Println("ms.SomeIntPtr", *ms.SomeIntPtr)
fmt.Println("ms.SomeStringPtr", *ms.SomeStringPtr)
This will output (try it on the Go Playground ):
ms.SomeIntPtr 3
ms.SomeStringPtr hi
So your code may look like this:
// assign "value" to "field":
if isPointer {
x := reflect.New(field.Type().Elem())
x.Elem().Set(reflect.ValueOf(value))
field.Set(x)
} else {
field.Set(reflect.ValueOf(value)) // works
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.