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
Like a struct an interface is created using the type keyword, followed by a name and the keyword interface . But instead of defining fields, we define a “method set”. A method set is a list of methods that a type must have in order to “implement” the interface.
Reflection in Go is built around three concepts: Types, Kinds, and Values. The reflect package in the standard library is the home for the types and functions that implement reflection in Go.
interface{} means you can put value of any type, including your own custom type. All types in Go satisfy an empty interface ( interface{} is an empty interface). In your example, Msg field can have value of any type.
Every type in golang, including user-defined type, itself has the information about type name, fields name and the function name. Golang reflection just reads these information or call the function. Through some mechanism, Golang can get the type name, storage size and so on.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With