简体   繁体   English

通过反射创建结构实例并设置值

[英]Create instance of struct via reflection and set values

what I try to do我想做什么

I try to pass an instance of a struct - including json tag s to a func , create a new instance , and set value on field我尝试将structinstance (包括json tag )传递给func ,创建一个新instance ,并在field上设置value
after this i try to serialize ( JSON ), but the values are empty在此之后我尝试序列化( JSON ),但值为空

NOTICE: i looked up loads of articles on SO about setting values via reflection, but it seems i missed a little detail注意:我在 SO 上查找了大量关于通过反射设置值的文章,但似乎我错过了一些细节

struct definition结构定义

this part defines the struct with json and xml tags这部分定义了带有 json 和 xml 标签的结构

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?之后我创建了一个实例并将其存储在一个interface{}中 - 为什么? because in my production code this stuff will be done in a func which accepts a interface{}因为在我的生产代码中,这些东西将在一个接受接口的func中完成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 ...在生产代码中,我用数据填充多个副本并将其添加到slice中......
but this only makes sense if the json tag s are kept in place and the stuff serializes just the same way.但这只有在json tag保持在适当位置并且东西以相同的方式序列化时才有意义。

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)
}

Your item is of type reflect.Value .您的item是类型reflect.Value You have to call Value.Interface() to obtain the value wrapped in it:您必须调用Value.Interface()来获取包含在其中的值:

fmt.Println("reflected: \n" + JSONify(item.Interface()))

With this change, output will be (try it on the Go Playground ):通过此更改,output 将是(在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本身也是一个结构,但显然试图封送它与封送Person结构值不同。 reflect.Value does not implement marshaling the wrapped data to JSON. reflect.Value不实现将包装数据编组到 JSON。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM