I'm testing this code snippet on go playground, I aim to use reflect to get fields from one object, and then set value to another object
package main
import (
"fmt"
"reflect"
)
type T struct {
A int `json:"aaa" test:"testaaa"`
B string `json:"bbb" test:"testbbb"`
}
type newT struct {
AA int
BB string
}
func main() {
t := T{
A: 123,
B: "hello",
}
tt := reflect.TypeOf(t)
tv := reflect.ValueOf(t)
newT := &newT{}
newTValue := reflect.ValueOf(newT)
for i := 0; i < tt.NumField(); i++ {
field := tt.Field(i)
newTTag := field.Tag.Get("newT")
tValue := tv.Field(i)
newTValue.Elem().FieldByName(newTTag).Set(tValue)
}
fmt.Println(newT)
}
And it gives a very strange error:
panic: reflect: call of reflect.flag.mustBeAssignable on zero Value
goroutine 1 [running]:
reflect.flag.mustBeAssignableSlow(0x0, 0x0)
/usr/local/go/src/reflect/value.go:240 +0xe0
reflect.flag.mustBeAssignable(...)
/usr/local/go/src/reflect/value.go:234
reflect.Value.Set(0x0, 0x0, 0x0, 0x100f80, 0x40a0f0, 0x82)
/usr/local/go/src/reflect/value.go:1531 +0x40
main.main()
/tmp/sandbox166479609/prog.go:32 +0x400
Program exited: status 2.
How to fix it?
First:
for i := 0; i < tt.NumField(); i++ {
field := tt.Field(i)
Each step here iterates through the fields of your instance of type T
. So the fields will be A
—or rather, the field descriptor whose Name
is A
and which describes an int with its json and test tags—and then B
(with the same picky details if we go any further).
Since both field descriptors have only two Get
-able items, you probably meant to use Get("test")
, as in Guarav Dhiman's answer.
If you do that, though, the result is "testaaa"
when you are on field A
and "testbbb"
when you are on field B
. If we annotate Guarav's code a bit more:
for i := 0; i < tt.NumField(); i++ {
field := tt.Field(i)
newTTag := field.Tag.Get("test")
fmt.Printf("newTTag = %#v\n", newTTag)
tValue := tv.Field(i)
newTfield := newTValue.Elem().FieldByName(newTTag)
fmt.Printf("newTfield = %#v\n", newTfield)
if newTfield.CanSet() {
newTfield.Set(tValue)
}
}
we will see this output:
newTTag = "testaaa"
newTfield = <invalid reflect.Value>
newTTag = "testbbb"
newTfield = <invalid reflect.Value>
What we need is to make the test
string in each tag name the field in the newT
type:
type T struct {
A int `json:"aaa" test:"AA"`
B string `json:"bbb" test:"BB"`
}
(Guarav actually already did this but did not mention it.) Now the program produces what (presumably) you intended:
&{123 hello}
The complete program, with commented-out tracing, is here.