【问题标题】:golang get a struct from an interface via reflectiongolang 通过反射从接口获取结构
【发布时间】:2015-12-14 17:13:54
【问题描述】:

原问题:

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

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

如果你直接传入结构体,play 示例就可以工作,但是,任何实现特定接口的东西都可以传入(在我的示例中,我只是使用空接口)。但是我不知道如何将其视为下面的结构。

更新:

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

【问题讨论】:

  • 您可以使用反射来查找结构上存在的成员的名称并获得对成员的访问权限。您实际上并没有得到结构的实例,因为正在使用的结构已编译;相反,您必须使用反射提供的结构属性的接口集。这些可以访问可用于操作底层结构的读取和设置函数(只要它是可修改的)。
  • 这就是我正在做的事情。 'interface{}' 中的包装纯粹是为了反映我在代码中遇到的问题而发明的。我确实找到了解决方法,但是 go 库似乎展示了这种通过 interface 将 interface{} 解包为具体类型的模式。 (golang.org/src/encoding/json/decode.go?s=2621:2669#L64)
  • 这个问题措辞不当,不能正确反映我遇到的问题(尽管我认为确实如此)。我正在努力更全面地重现问题并阅读json解码部分。感谢大家的投入,准备好后我会发布一个新问题并在此处放置链接。感谢大家的意见。

标签: reflection go


【解决方案1】:

你不想传递一个指向接口的指针,你想传递一个指向你的结构本身的指针。

robot := &Robot{}
f(robot)

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

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

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

【讨论】:

  • 这不能回答问题。我想知道如何传入接口并访问底层结构。
  • @Gary:如果不传入指针,则无法访问底层结构。再次将其包装在接口中是多余的,因为它已在函数调用中放入interface{}
  • 这是如何工作的:74 func Unmarshal(data []byte, v interface{}) error {
  • @Gary,我猜你指的是 json 包??,如果你不传入一个指针,你会得到一个错误:play.golang.org/p/55mMqnZt8c
  • @Gary,指向接口的指针大多是无用的,而且几乎总是一个错误。指向接口的指针不会为您提供指向其值的指针;如果没有指向该结构的指针,则无法修改结构字段。
【解决方案2】:

在您的原始代码中,使用:

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

说明
在原始版本中,我们发送接口变量的地址(这是机器人的副本)。这会发送一个接口类型的指针,因此反射作用于机器人的副本。

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

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

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

【讨论】:

  • 请解释为什么这会对用户有所帮助。
  • 已添加说明。
【解决方案3】:

你可以使用类型断言。

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

来自这个stackoverflow post

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-11
    • 2010-10-07
    • 2017-12-11
    • 2016-10-15
    • 2016-10-07
    • 2016-09-30
    • 2010-09-09
    • 1970-01-01
    相关资源
    最近更新 更多