Serializing/Deserializing a complex GO struct to store externally (i.e. in a database, redis, or text file)

Sometimes one needs to serialize data to store outside of a go application. One common approach is to save it as a JSON blob which can have it’s own challenges when retrieving it. In this article I will show you how to make use of GOB to serialize a complex struct into textual data than can be stored in any data store and then returned to its original structure when decoded.

package main

import (
	"bytes"
	"context"
	"encoding/base64"
	"encoding/gob"
	"log"

	"googlemaps.github.io/maps"

	"github.com/davecgh/go-spew/spew"
)

func main() {
	sourceStruct := getMap()

	spew.Dump(sourceStruct) // Show the original structure to be encoded

	valueToEncode := encodeToGob(&sourceStruct)

	encodedBase64Value := base64encode(valueToEncode)

	spew.Dump(encodedBase64Value) // Show the encoded value

	/* Now that everything has been encoded we can decode it and we should get back the sane struct that we encoded */

	/* We need to make the gob package aware of what types we've encoded so that we can decode it. */
	gob.Register([]maps.GeocodingResult{})

	var decodedStruct []maps.GeocodingResult

	decodedValue := base64decode(encodedBase64Value)

	decodeFromGob(decodedValue, &decodedStruct)

	spew.Dump(decodedStruct) // Show the decoded structure, which should be the same as the original

	/* Success */
}

func getMap() []maps.GeocodingResult {
	APIKey := "[Google API KEY]"

	c, _ := maps.NewClient(
		maps.WithAPIKey(APIKey),
	)

	r := &maps.GeocodingRequest{
		Address: "1600 Pennsylvania Ave NW",
		Region:  "us",
	}

	response, err := c.Geocode(context.Background(), r)
	if err != nil {
		log.Fatal(err)
	}

	return response
}

func encodeToGob(source interface{}) []byte {
	var buff bytes.Buffer
	encoder := gob.NewEncoder(&buff)
	err := encoder.Encode(source)
	if err != nil {
		log.Fatal(err)
	}
	return buff.Bytes()
}

func decodeFromGob(source []byte, destination interface{}) {
	decoder := gob.NewDecoder(bytes.NewBuffer(source))
	err := decoder.Decode(destination)
	if err != nil {
		log.Fatal(err)
	}
}

func base64encode(source []byte) []byte {
	encodedBytes := make([]byte, base64.URLEncoding.EncodedLen(len(source)))
	base64.URLEncoding.Encode(encodedBytes, source)

	return encodedBytes
}

func base64decode(source []byte) []byte {
	decodedBytes := make([]byte, base64.URLEncoding.DecodedLen(len(source)))
	_, err := base64.URLEncoding.Decode(decodedBytes, source)
	if err != nil {
		log.Fatal(err)
	}
	return decodedBytes
}

Comments & Responses

Leave a Reply

Your email address will not be published. Required fields are marked *