英文:
golang get a struct from an interface via reflection
问题
我正在尝试进行一些反序列化操作,但对于在接口中传递结构体时如何访问结构体感到有些困惑。
在示例代码中,如果直接传递结构体,playground上的示例可以正常工作。然而,由于可能传递任何实现特定接口的内容(在我的示例中,我只是使用了空接口),我无法弄清楚如何将其视为结构体。
在更新的代码中,您可以使用反射来访问结构体的字段和标签。您可以使用reflect.ValueOf
获取接口的值,并使用Elem
方法获取指针指向的值。然后,您可以使用FieldByName
方法获取特定字段的值,并使用Addr
方法获取该字段的地址。最后,您可以使用Interface
方法将地址转换为指针,并对其进行修改。
在您的代码中,您可以尝试使用以下方法:
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
}
这样,您就可以通过接口访问结构体的字段并进行修改。
希望这可以帮助到您!如果您有任何其他问题,请随时问我。
英文:
Original question:
I'm trying to do some deserialization and I'm a little confused about how to access a struct when passing in an interface.
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
The play example works if you just pass in the struct directly, however as it's possible for anything to be passed in that implements a specific interface (in my example case I'm just using the empty interface). However I can't figure out how to then treat it as a struct underneath.
Updated:
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
I got the idea of copying my struct and thereby getting an addressable instance from here: https://gist.github.com/hvoecking/10772475
So I guess my question is now, is there a mechanism to access an addressable / settable struct without having to resort to a copy?
The underlying problem is taking strings (byte arrays really) and having a struct have the necessary info to effectively deserialize it without having to write a couple dozen deserialization functions which would suck to maintain. So the tags in those sample structs aren't addressed in the sample question, but accessing the structs tag fields would provide the offsets from which to populate the struct from the input bytes. Obviously I haven't gotten that far. Part of my frustration here is that it seems I've worked very hard to not get very far and I don't feel like i've learned much in the process.
Some additional play edits that got me my tags back:
http://play.golang.org/p/2DbbWLDKPI
答案1
得分: 5
你不想传递一个指向接口的指针,而是想传递一个指向你的结构体本身的指针。
robot := &Robot{}
f(robot)
当你将 robot
赋值给 iface
时,你创建了 robot
值的一个副本。从 iface
中永远无法获取对 robot
的引用。
当你传递 f(&iface)
时,调用 reflect.ValueOf(i).Elem()
只是返回内部的 iface
值,而不是一个 Robot
结构体值。
英文:
You don't want to pass a pointer to the interface, you want to pass in a pointer to your struct itself.
robot := &Robot{}
f(robot)
http://play.golang.org/p/owv-Y4dnkl
The moment you assigned robot
to iface
, you created a copy of the robot
value. There's is no way to ever get a reference back to robot
from iface
.
When you pass in f(&iface)
, the call to reflect.ValueOf(i).Elem()
is just returning the inner iface
value, not a Robot
struct value.
答案2
得分: 1
在你的原始代码中,使用:
var iface interface{} = &robot
f(iface)
解释:
在原始版本中,我们发送的是接口变量的地址(它是 robot 的副本)。这样发送的是一个接口类型的指针,因此 reflect 在 robot 的副本上工作。
var iface interface{} = robot
f(&iface)
我们需要做的是将一个 robot 类型的指针赋值给接口变量。这样,当我们发送接口时,我们发送的是 robot 类型的指针,因此 reflect 就可以使用实际的 robot 对象而不是副本。
var iface interface{} = &robot
f(iface)
英文:
In your original code, use:
var iface interface{} = &robot
f(iface)
Explanation<br/>
In the original version, we are sending in the address of the interface variable (which is a copy of the robot). This sends a pointer of type interface, and so reflect works on the copy of the robot.
var iface interface{} = robot
f(&iface)
What we need to do, is assign a pointer of type robot to the interface variable. Thus, when we send the interface we are sending the pointer of type robot, so reflect works with the actual robot object and not a copy.
var iface interface{} = &robot
f(iface)
答案3
得分: -1
你可以使用类型断言。
value, ok := i.(Robot)
if ok {
fmt.Println(value.Id)
}
来自这个stackoverflow帖子。
英文:
You could use Type Assertion.
value, ok := i(Robot)
if ok {
fmt.Println(value.Id)
}
From this stackoverflow post.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论