gogob

Golang Gob decoding does not decode array of []byte


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.


Solution

  • 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.