通过反射从接口中获取一个结构体的方法是:

huangapple go评论86阅读模式
英文:

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(&amp;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{} = &amp;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.

huangapple
  • 本文由 发表于 2015年12月15日 01:13:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/34272837.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定