英文:
Create instance of struct via reflection and set values
问题
我尝试做什么
我试图传递一个包括json标签的struct的实例给一个func,创建一个新的实例,并在字段上设置值。
之后,我尝试序列化(JSON),但值为空。
注意:我查阅了很多关于通过反射设置值的文章,但似乎我错过了一些细节。
struct定义
这部分定义了带有json和xml标签的结构体
type Person struct {
Name string `json:"Name" xml:"Person>FullName"`
Age int `json:"Age" xml:"Person>Age"`
}
创建实例(+包装到空接口中)
之后,我创建一个实例并将其存储在interface{}中 - 为什么?因为在我的生产代码中,这些东西将在接受interface{}的func中完成。
var iFace interface{} = Person{
Name: "Test",
Age: 666,
}
创建结构体的新实例并通过反射设置值
iFaceType := reflect.TypeOf(iFace)
item := reflect.New(iFaceType)
s := item.Elem()
if s.Kind() == reflect.Struct {
fName := s.FieldByName("Name")
if fName.IsValid() {
// 只有当Value是可寻址的并且不是通过未导出的结构字段获得时,才能更改Value。
if fName.CanSet() {
// 更改N的值
switch fName.Kind() {
case reflect.String:
fName.SetString("reflectedNameValue")
fmt.Println("Name was set to reflectedNameValue")
}
}
}
fAge := s.FieldByName("Age")
if fAge.IsValid() {
// 只有当Value是可寻址的并且不是通过未导出的结构字段获得时,才能更改Value。
if fAge.CanSet() {
// 更改N的值
switch fAge.Kind() {
case reflect.Int:
x := int64(42)
if !fAge.OverflowInt(x) {
fAge.SetInt(x)
fmt.Println("Age was set to", x)
}
}
}
}
}
问题
我做错了什么?
在生产代码中,我用数据填充多个副本并将其添加到slice中...
但只有在json标签保持不变且序列化方式完全相同的情况下,这才有意义。
用于测试的代码示例
package main
import (
"encoding/json"
"fmt"
"reflect"
)
func main() {
type Person struct {
Name string `json:"Name" xml:"Person>FullName"`
Age int `json:"Age" xml:"Person>Age"`
}
var iFace interface{} = Person{
Name: "Test",
Age: 666,
}
fmt.Println("normal: \n" + JSONify(iFace))
iFaceType := reflect.TypeOf(iFace)
item := reflect.New(iFaceType)
s := item.Elem()
if s.Kind() == reflect.Struct {
fName := s.FieldByName("Name")
if fName.IsValid() {
// 只有当Value是可寻址的并且不是通过未导出的结构字段获得时,才能更改Value。
if fName.CanSet() {
// 更改N的值
switch fName.Kind() {
case reflect.String:
fName.SetString("reflectedNameValue")
fmt.Println("Name was set to reflectedNameValue")
}
}
}
fAge := s.FieldByName("Age")
if fAge.IsValid() {
// 只有当Value是可寻址的并且不是通过未导出的结构字段获得时,才能更改Value。
if fAge.CanSet() {
// 更改N的值
switch fAge.Kind() {
case reflect.Int:
x := int64(42)
if !fAge.OverflowInt(x) {
fAge.SetInt(x)
fmt.Println("Age was set to", x)
}
}
}
}
}
fmt.Println("reflected: \n" + JSONify(item))
}
func JSONify(v interface{}) string {
var bytes []byte
bytes, _ = json.MarshalIndent(v, "", "\t")
return string(bytes)
}
英文:
what I try to do
I try to pass an instance of a struct - including json tags to a func, create a new instance, and set value on field
after this i try to serialize (JSON), but the values are empty
NOTICE: i looked up loads of articles on SO about setting values via reflection, but it seems i missed a little detail
struct definition
this part defines the struct with json and xml tags
type Person struct {
Name string `json:"Name" xml:"Person>FullName"`
Age int `json:"Age" xml:"Person>Age"`
}
create instance (+wrapping into empty interface)
afterwards I create an instance and store it in an interface{} - why? because in my production code this stuff will be done in a func which accepts a interface{}
var iFace interface{} = Person{
Name: "Test",
Age: 666,
}
creating a new instance of the struct and setting values via reflection
iFaceType := reflect.TypeOf(iFace)
item := reflect.New(iFaceType)
s := item.Elem()
if s.Kind() == reflect.Struct {
fName := s.FieldByName("Name")
if fName.IsValid() {
// A Value can be changed only if it is
// addressable and was not obtained by
// the use of unexported struct fields.
if fName.CanSet() {
// change value of N
switch fName.Kind() {
case reflect.String:
fName.SetString("reflectedNameValue")
fmt.Println("Name was set to reflectedNameValue")
}
}
}
fAge := s.FieldByName("Age")
if fAge.IsValid() {
// A Value can be changed only if it is
// addressable and was not obtained by
// the use of unexported struct fields.
if fAge.CanSet() {
// change value of N
switch fAge.Kind() {
case reflect.Int:
x := int64(42)
if !fAge.OverflowInt(x) {
fAge.SetInt(x)
fmt.Println("Age was set to", x)
}
}
}
}
}
Question
what am I doing wrong?
in production code I fill multiple copies with data and add it to a slice...
but this only makes sense if the json tags are kept in place and the stuff serializes just the same way.
code sample for play
package main
import (
"encoding/json"
"fmt"
"reflect"
)
func main() {
type Person struct {
Name string `json:"Name" xml:"Person>FullName"`
Age int `json:"Age" xml:"Person>Age"`
}
var iFace interface{} = Person{
Name: "Test",
Age: 666,
}
fmt.Println("normal: \n" + JSONify(iFace))
iFaceType := reflect.TypeOf(iFace)
item := reflect.New(iFaceType)
s := item.Elem()
if s.Kind() == reflect.Struct {
fName := s.FieldByName("Name")
if fName.IsValid() {
// A Value can be changed only if it is
// addressable and was not obtained by
// the use of unexported struct fields.
if fName.CanSet() {
// change value of N
switch fName.Kind() {
case reflect.String:
fName.SetString("reflectedNameValue")
fmt.Println("Name was set to reflectedNameValue")
}
}
}
fAge := s.FieldByName("Age")
if fAge.IsValid() {
// A Value can be changed only if it is
// addressable and was not obtained by
// the use of unexported struct fields.
if fAge.CanSet() {
// change value of N
switch fAge.Kind() {
case reflect.Int:
x := int64(42)
if !fAge.OverflowInt(x) {
fAge.SetInt(x)
fmt.Println("Age was set to", x)
}
}
}
}
}
fmt.Println("reflected: \n" + JSONify(item))
}
func JSONify(v interface{}) string {
var bytes []byte
bytes, _ = json.MarshalIndent(v, "", "\t")
return string(bytes)
}
答案1
得分: 3
您的item是类型为reflect.Value。您需要调用Value.Interface()来获取其中包装的值:
fmt.Println("reflected: \n" + JSONify(item.Interface()))
通过这个更改,输出将会是(在Go Playground上尝试):
normal:
{
"Name": "Test",
"Age": 666
}
Name was set to reflectedNameValue
Age was set to 42
reflected:
{
"Name": "reflectedNameValue",
"Age": 42
}
reflect.Value本身也是一个结构体,但显然尝试将其编组为JSON与编组Person结构体值不同。reflect.Value不实现将包装数据编组为JSON的功能。
英文:
Your item is of type reflect.Value. You have to call Value.Interface() to obtain the value wrapped in it:
fmt.Println("reflected: \n" + JSONify(item.Interface()))
With this change, output will be (try it on the Go Playground):
normal:
{
"Name": "Test",
"Age": 666
}
Name was set to reflectedNameValue
Age was set to 42
reflected:
{
"Name": "reflectedNameValue",
"Age": 42
}
reflect.Value itself is also a struct, but obviously trying to marshal it will not be identical to marshaling a Person struct value. reflect.Value does not implement marshaling the wrapped data to JSON.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论