golang通过反射从接口获取结构

时间:2015-12-14 17:13:55

标签: reflection go

原始问题:

我正在尝试进行一些反序列化,并且在传入接口时,我对如何访问结构有点困惑。

package main

import (
    "fmt"
    "reflect"
)

type Robot struct {
    Id int
}

func f(i interface{}) {
    v := reflect.ValueOf(i).Elem().FieldByName("Id")
    fmt.Println("fields: ", reflect.ValueOf(i).Elem().NumField()) 
    ptr := v.Addr().Interface().(*int)
    *ptr = 100
}

func main() {
    robot := Robot{}

    var iface interface{} = robot // if I omit this line and pass in robot this works
    f(&iface)
    fmt.Println(robot.Id) //I want to get here 100

}

http://play.golang.org/p/y6UN3KZxRB

如果您直接传入结构,则播放示例有效,但是因为可以传递任何实现特定接口的内容(在我的示例中,我只是使用空接口)。但是,我无法弄清楚如何将其视为下面的结构。

更新:

package main

import (
    "fmt"
    "reflect"
)

type MessageOne struct {
    Header   string `FixedWidth:0,4`
    FieldOne string `FixedWidth:"4,4"`
    FieldTwo string `FixedWidth:"8,4"`
}

type MessageTwo struct {
    FieldX string `FixedWidth:"X,Y"`
    FieldY string `FixedWidth:"X,Y"`
}

var (
    messageMap = map[string]interface{}{
        "msg1": MessageOne{FieldOne: "testValueUnchanged"},
        "msg2": MessageTwo{},
    }
)

func deserialize(input string, i interface{}) interface{} {
    value := reflect.ValueOf(i)
    fmt.Println("1st Value Type: ", value.Kind())
    // unswarp ptr
    value = value.Elem()
    fmt.Println("Unwrapped: ", value.Kind())
    value = value.Elem()
    fmt.Println("Unwrapped: ", value.Kind())

    // Create a copy that I can set?
    copyValue := reflect.New(value.Type()).Elem()
    fmt.Println("Orig Struct is settable", value.CanSet())
    fmt.Println("Orig StructField0 is settable", value.Field(0).CanSet())

    fmt.Println("Copy is: ", copyValue.Kind())
    fmt.Println("Copy Struct is settable", copyValue.CanSet())
    fmt.Println("Copy StructField0 is settable", copyValue.Field(0).CanSet())
    fmt.Println("Orig struct type is: ", value.Type())
    fmt.Println("Copy struct type is: ", copyValue.Type())

    copyValue.Field(1).SetString("testValueChanged")

    return copyValue.Interface()
}

func GetMessageFromInput(input string) interface{} {
    selector := input[0:4]
    fmt.Println(selector)
    field := messageMap[selector]
    return deserialize(input, &field)
}

func main() {
    val := messageMap["msg1"]

    serializedData := "msg1.012345678"

    deserializedVal := GetMessageFromInput(serializedData)

    //msg1 := deserializedVal.(MessageOne)

    fmt.Printf("Orig: %+v \nReceived: %+v", val, deserializedVal)
}

http://play.golang.org/p/Cj9oPPGSLM

我想到了复制我的结构,从而从这里获得一个可寻址的实例:https://gist.github.com/hvoecking/10772475

所以我想我现在的问题是,是否有一种机制来访问可寻址/可设置的结构而不必诉诸副本?

潜在的问题是获取字符串(字节数组真的)并且有一个结构体具有必要的信息来有效地反序列化它而不必编写几十个反序列化函数,这些函数很难维护。因此,示例问题中未解决这些示例结构中的标记,但访问结构标记字段将提供从输入字节填充结构的偏移量。显然我还没有那么远。我在这里感到沮丧的部分原因是,我似乎很努力地走得很远,而且我觉得我在这个过程中学到的东西并不多。

一些额外的播放编辑让我的标签回复: http://play.golang.org/p/2DbbWLDKPI

3 个答案:

答案 0 :(得分:3)

您不希望传递指向接口的指针,您希望传入指向结构本身的指针。

robot := &Robot{}
f(robot)

http://play.golang.org/p/owv-Y4dnkl

您将robot分配给iface的那一刻,您创建了robot值的副本。无法从robot获取iface的引用。

当您传递f(&iface)时,对reflect.ValueOf(i).Elem()的调用只返回内部iface值,而不是Robot结构值。

答案 1 :(得分:1)

在原始代码中,使用:

var iface interface{} = &robot
f(iface)

说明
在原始版本中,我们正在发送接口变量的地址(这是机械手的副本)。这会发送一个类型为interface的指针,从而反映对机器人副本的工作。

var iface interface{} = robot
f(&iface)

我们需要做的是将机械手类型的指针分配给接口变量。因此,当我们发送接口时,我们发送的是机械手类型的指针,因此反射将与实际的机械手对象一起工作,而不是副本。

var iface interface{} = &robot
f(iface)

答案 2 :(得分:0)

您可以使用Type Assertion。

value, ok := i(Robot)
if ok {
    fmt.Println(value.Id)
}

来自stackoverflow post