I am trying to understand how the GOB package works in GoLang. For better understanding, I want to serialize a structure into a byte buffer and later deserialize the buffer back into a structure of the same type. I want to write a generic code that should deserialize any type, e.g. Product or Company or Item or WebOffer structs. I want to use this approach because different kind of data are streamed and the program does not know the actual type of data.
Currently I am using the Product structure and I do not want to specify the type of the structure at the time of deserialization. If you see the below commented code which works as expected because I have passed the actual type of the structure for the deserialization.
var p1 Product
err = deserializeGob(data, &p1)
On the other hand, if I use -
decodedData, err := deserializeGob_AnotherWay(data)
the above function call throws an error - Error deserializing: error deserializing: gob: local interface type *interface {} can only be decoded from remote interface type; received concrete type Product = struct { Name string; Price float; Discount ProductDiscount = struct { Percentage float; }; }
I could not find the cause of this error. I would appreciate your help.
Complete code -
package main
import (
"bytes"
"encoding/gob"
"fmt"
)
// Product struct
type Product struct {
Name string
Price float64
Discount ProductDiscount
}
// ProductDiscount struct
type ProductDiscount struct {
Percentage float64
}
func serialize(prod interface{}) ([]byte, error) {
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
err := enc.Encode(prod)
if err != nil {
return nil, fmt.Errorf("error serializing: %w", err)
}
return buf.Bytes(), nil
}
/*func deserializeGob(data []byte, v interface{}) (error) {
buf := bytes.NewReader(data)
dec := gob.NewDecoder(buf)
err := dec.Decode(v)
if err != nil {
return fmt.Errorf("error deserializing: %w", err)
}
return nil
}*/
func deserializeGob_AnotherWay(data []byte) (interface{}, error) {
buf := bytes.NewReader(data)
dec := gob.NewDecoder(buf)
var v interface{}
err := dec.Decode(&v)
if err != nil {
return nil, fmt.Errorf("error deserializing: %w", err)
}
// Type assertion to get the original Product type
switch v := v.(type) {
case Product:
return v, nil
default:
return nil, fmt.Errorf("unexpected type: %T", v)
}
}
func main() {
var d ProductDiscount
d.Percentage = 10
prod := Product{
Name: "CarToy1",
Price: 30,
Discount: d,
}
data, err := serialize(prod)
if err != nil {
fmt.Println("Error serializing:", err)
return
}
fmt.Println("Serialized data:", data)
/*var p1 Product
err = deserializeGob(data, &p1)
if err != nil {
fmt.Println("Error deserializing:", err)
return
}
fmt.Println("Deserialized product:", p1)*/
decodedData, err := deserializeGob_AnotherWay(data)
if err != nil {
fmt.Println("Error deserializing:", err)
return
}
// Type assertion to get the original Product type
p2, ok := decodedData.(Product)
if !ok {
fmt.Println("Deserialized data is not of type product")
return
}
fmt.Println("Deserialized product:", p2)
}
It was missing a derefencing before the enc.Encode(&prod)
and also the struct must be registered with gob.Register(Product{})
both for serialization and deserialization.
package main
import (
"bytes"
"encoding/gob"
"fmt"
)
// Product struct
type Product struct {
Name string
Price float64
Discount ProductDiscount
}
// ProductDiscount struct
type ProductDiscount struct {
Percentage float64
}
func serialize(prod interface{}) ([]byte, error) {
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
err := enc.Encode(&prod)
if err != nil {
return nil, fmt.Errorf("error serializing: %w", err)
}
return buf.Bytes(), nil
}
func deserializeGob_AnotherWay(data []byte) (interface{}, error) {
buf := bytes.NewReader(data)
dec := gob.NewDecoder(buf)
var v interface{}
err := dec.Decode(&v)
if err != nil {
return nil, fmt.Errorf("error deserializing: %w", err)
}
// Type assertion to get the original Product type
switch v := v.(type) {
case Product:
return v, nil
default:
return nil, fmt.Errorf("unexpected type: %T", v)
}
}
func main() {
var d ProductDiscount
d.Percentage = 10
prod := Product{
Name: "CarToy1",
Price: 30,
Discount: d,
}
gob.Register(Product{})
data, err := serialize(prod)
if err != nil {
fmt.Println("Error serializing:", err)
return
}
fmt.Println("Serialized data:", data)
decodedData, err := deserializeGob_AnotherWay(data)
if err != nil {
fmt.Println("Error deserializing:", err)
return
}
// Type assertion to get the original Product type
p2, ok := decodedData.(Product)
if !ok {
fmt.Println("Deserialized data is not of type product")
return
}
fmt.Println("Deserialized product:", p2)
}
You might also make it a bit shorter with the new any
generics type.
package main
import (
"bytes"
"encoding/gob"
"fmt"
)
// Product struct
type Product struct {
Name string
Price float64
Discount ProductDiscount
}
// ProductDiscount struct
type ProductDiscount struct {
Percentage float64
}
func serialize(prod any) ([]byte, error) {
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
err := enc.Encode(&prod)
if err != nil {
return nil, fmt.Errorf("error serializing: %w", err)
}
return buf.Bytes(), nil
}
func deserializeGob_AnotherWay(data []byte) (any, error) {
buf := bytes.NewReader(data)
dec := gob.NewDecoder(buf)
var v any
err := dec.Decode(&v)
if err != nil {
return nil, fmt.Errorf("error deserializing: %w", err)
}
return v, err
}
func main() {
var d ProductDiscount
d.Percentage = 10
prod := Product{
Name: "CarToy1",
Price: 30,
Discount: d,
}
gob.Register(Product{})
data, err := serialize(prod)
if err != nil {
fmt.Println("Error serializing:", err)
return
}
fmt.Println("Serialized data:", data)
decodedData, err := deserializeGob_AnotherWay(data)
if err != nil {
fmt.Println("Error deserializing:", err)
return
}
// Type assertion to get the original Product type
p2, ok := decodedData.(Product)
if !ok {
fmt.Println("Deserialized data is not of type product")
return
}
fmt.Println("Deserialized product:", p2)
}