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?
It is as error call of reflect.flag.mustBeAssignable on zero Value
says , newTValue.Elem().FieldByName(newTTag).CanSet()
returns false in your your code and according to documentation
Set assigns x to the value v. It panics if CanSet returns false. As in Go, x's value must be assignable to v's type.
This is corrected code that takes fields from one Object and assigns value to another one.
package main
import (
"fmt"
"reflect"
)
type T struct {
A int `json:"aaa" test:"AA"`
B string `json:"bbb" test:"BB"`
}
type newT struct {
AA int
BB string
Testaaa 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("test")
tValue := tv.Field(i)
newTfield := newTValue.Elem().FieldByName(newTTag)
if newTfield.CanSet() {
newTfield.Set(tValue)
}
}
fmt.Println(newT)
}
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 .
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.