英文:
Golang Gob decoding does not decode array of []byte
问题
我正在尝试解码一个Inv
结构体,但是解码相同的编码值返回了不同的值。
// inv 结构体
type Inv struct {
AddrFrom string
Type int
data [][]byte
}
inv := Inv{
AddrFrom: nodeAddress,
Type: kind,
data: inventories,
}
data := GobEncode(inv)
var payload Inv
gob.NewDecoder(bytes.NewBuffer(data)).Decode(&payload)
这里 payload
和 inv
有不同的值。当解码 inv
结构体的 data
字段时,其长度为零。
英文:
I am trying to decode an Inv
struct, but decoding the same encoded value returns a different value.
// inv struct
type Inv struct {
AddrFrom string
Type int
data [][]byte
}
inv := Inv{
AddrFrom: nodeAddress,
Type: kind,
data: inventories,
}
data := GobEncode(inv)
var payload Inv
gob.NewDecoder(bytes.NewBuffer(data)).Decode(&payload)
Here payload and inv have different values. When decoded data field of inv struct is of length zero.
答案1
得分: 0
https://pkg.go.dev/encoding/gob
> chan类型或func类型的结构字段被视为未导出字段并被忽略。
https://go.dev/ref/spec#Exported_identifiers
> 为了允许从另一个包中访问标识符,可以将其导出。如果满足以下两个条件,则标识符被导出:
> - 标识符名称的第一个字符是一个Unicode大写字母(Unicode类“Lu”);
> - 标识符在包块中声明,或者是字段名或方法名。
>
> 所有其他标识符都不会被导出。
https://pkg.go.dev/encoding/gob#hdr-Types_and_Values
> Gob可以通过调用相应的方法(按照首选顺序)对实现了GobEncoder或encoding.BinaryMarshaler接口的任何类型的值进行编码。
在内部,gob包依赖于reflect包,后者设计时遵循可见性原则。因此,gob包不会自动处理那些字段,而是需要你编写专门的实现。
https://pkg.go.dev/encoding/gob#GobEncoder
> GobEncoder是描述数据的接口,该数据为编码值提供自己的表示形式,以便传输给GobDecoder。实现GobEncoder和GobDecoder的类型对其数据的表示具有完全控制权,因此可以包含诸如私有字段、通道和函数等通常不可传输的内容。
示例
package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
// Vector类型具有未导出字段,包无法访问该字段。
// 因此,我们编写了BinaryMarshal/BinaryUnmarshal方法对该类型进行编码和解码,
// 以便我们可以使用gob包发送和接收该类型。这些接口在"encoding"包中定义。
// 我们也可以使用本地定义的GobEncode/GobDecoder接口。
type Vector struct {
x, y, z int
}
func (v Vector) MarshalBinary() ([]byte, error) {
// 简单的编码:纯文本。
var b bytes.Buffer
fmt.Fprintln(&b, v.x, v.y, v.z)
return b.Bytes(), nil
}
// UnmarshalBinary修改接收器,因此必须使用指针接收器。
func (v *Vector) UnmarshalBinary(data []byte) error {
// 简单的编码:纯文本。
b := bytes.NewBuffer(data)
_, err := fmt.Fscanln(b, &v.x, &v.y, &v.z)
return err
}
// 此示例传输实现了自定义编码和解码方法的值。
func main() {
var network bytes.Buffer // 用于模拟网络。
// 创建编码器并发送一个值。
enc := gob.NewEncoder(&network)
err := enc.Encode(Vector{3, 4, 5})
if err != nil {
log.Fatal("encode:", err)
}
// 创建解码器并接收一个值。
dec := gob.NewDecoder(&network)
var v Vector
err = dec.Decode(&v)
if err != nil {
log.Fatal("decode:", err)
}
fmt.Println(v)
}
由于字段类型已经是字节切片,你实际上只是遇到了可见性访问问题和所需的专用编组实现,尽管可以导出该字段,但编写专用实现应该很简单。
英文:
https://pkg.go.dev/encoding/gob
> A struct field of chan or func type is treated exactly like an unexported field and is ignored.
https://go.dev/ref/spec#Exported_identifiers
> An identifier may be exported to permit access to it from another package. An identifier is exported if both:
> - the first character of the identifier's name is a Unicode upper case letter (Unicode class "Lu"); and
> - the identifier is declared in the package block or it is a field name or method name.
>
> All other identifiers are not exported.
https://pkg.go.dev/encoding/gob#hdr-Types_and_Values
> Gob can encode a value of any type implementing the GobEncoder or encoding.BinaryMarshaler interfaces by calling the corresponding method, in that order of preference.
Internally, the gob package relies on the reflect package, which is designed to respect the visibility principle. Thus the gob package does not handle those fields automatically, it requires you to write dedicated implementation.
https://pkg.go.dev/encoding/gob#GobEncoder
> GobEncoder is the interface describing data that provides its own representation for encoding values for transmission to a GobDecoder. A type that implements GobEncoder and GobDecoder has complete control over the representation of its data and may therefore contain things such as private fields, channels, and functions, which are not usually transmissible in gob streams.
Example
package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
// The Vector type has unexported fields, which the package cannot access.
// We therefore write a BinaryMarshal/BinaryUnmarshal method pair to allow us
// to send and receive the type with the gob package. These interfaces are
// defined in the "encoding" package.
// We could equivalently use the locally defined GobEncode/GobDecoder
// interfaces.
type Vector struct {
x, y, z int
}
func (v Vector) MarshalBinary() ([]byte, error) {
// A simple encoding: plain text.
var b bytes.Buffer
fmt.Fprintln(&b, v.x, v.y, v.z)
return b.Bytes(), nil
}
// UnmarshalBinary modifies the receiver so it must take a pointer receiver.
func (v *Vector) UnmarshalBinary(data []byte) error {
// A simple encoding: plain text.
b := bytes.NewBuffer(data)
_, err := fmt.Fscanln(b, &v.x, &v.y, &v.z)
return err
}
// This example transmits a value that implements the custom encoding and decoding methods.
func main() {
var network bytes.Buffer // Stand-in for the network.
// Create an encoder and send a value.
enc := gob.NewEncoder(&network)
err := enc.Encode(Vector{3, 4, 5})
if err != nil {
log.Fatal("encode:", err)
}
// Create a decoder and receive a value.
dec := gob.NewDecoder(&network)
var v Vector
err = dec.Decode(&v)
if err != nil {
log.Fatal("decode:", err)
}
fmt.Println(v)
}
As the field type is already a byte slice, you really are just hitting a visibility access issue and the dedicated required marshalling implementation, while arguable because you could as well export that field, should be straightforward.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论