我正在尝试编写允许我将简单结构编组/解组成字节数组的函数。我已经成功地写了Marshal
,在#go-nuts的善良人士的帮助下,但我在编写Unmarshal
时遇到了麻烦。
// Unmarshal unpacks the binary data and stores it in the packet using
// reflection.
func Unmarshal(b []byte, t reflect.Type) (pkt interface{}, err error) {
buf := bytes.NewBuffer(b)
p := reflect.New(t)
v := reflect.ValueOf(p)
for i := 0; i < t.NumField(); i++ {
f := v.Field(i)
switch f.Kind() {
case reflect.String:
// length of string
var l int16
var e error
e = binary.Read(buf, binary.BigEndian, &l)
if e != nil {
err = e
return
}
// read length-of-string bytes from the buffer
raw := make([]byte, l)
_, e = buf.Read(raw)
if e != nil {
err = e
return
}
// convert the bytes to a string
f.SetString(bytes.NewBuffer(raw).String())
default:
e := binary.Read(buf, binary.BigEndian, f.Addr())
if e != nil {
err = e
return
}
}
}
pkt = p
return
}
上面代码的问题在于,在结尾附近调用f.Addr()
显然是在尝试获取无法寻址的值的地址。
如果有替代解决方案,我也会感激。无论哪种方式,任何帮助将非常感激。
谢谢!
答案 0 :(得分:1)
我认为你应该使用
v := p.Elem() // Get the value that 'p' points to
而不是
v := reflect.ValueOf(p)
答案 1 :(得分:0)
具有大量假设和简单数据格式的工作示例:
package main
import (
"fmt"
"reflect"
"strconv"
)
// example marshalled format. lets say that marshalled data will have
// four bytes of a formatted floating point number followed by two more
// printable bytes.
type m42 []byte
// example struct we'd like to unmarshal into.
type packet struct {
S string // exported fields required for reflection
F float64
}
// example usage
func main() {
var p packet
if err := Unmarshal(m42("3.14Pi"), &p); err == nil {
fmt.Println(p)
} else {
fmt.Println(err)
}
}
func Unmarshal(data m42, structPtr interface{}) error {
vp := reflect.ValueOf(structPtr)
ve := vp.Elem() // settable struct Value
vt := ve.Type() // type info for struct
nStructFields := ve.NumField()
for i := 0; i < nStructFields; i++ {
fv := ve.Field(i) // settable field Value
sf := vt.Field(i) // StructField type information
// struct field name indicates which m42 field to unmarshal.
switch sf.Name {
case "S":
fv.SetString(string(data[4:6]))
case "F":
s := string(data[0:4])
if n, err := strconv.ParseFloat(s, 64); err == nil {
fv.SetFloat(n)
} else {
return err
}
}
}
return nil
}
适当的替代解决方案在很大程度上取决于您需要支持的实际数据。
答案 2 :(得分:0)
我打算说f.Addr()之所以有问题是因为它实际上是不可寻址的。
反射包Type对象有一个方法可以告诉您类型是否可寻址,称为CanAddr()。假设字段是可寻址的,如果它不是字符串并不总是正确的。如果结构没有作为指向结构的指针传入,那么它的字段将不可寻址。有关可以和不可寻址的更多详细信息,请参阅:http://weekly.golang.org/pkg/reflect/#Value.CanAddr,其中概述了正确的规则。
基本上为了让你的代码工作,我认为你需要确保你总是用指向结构的指针来调用它。