Skip to main content
โšก Calmops

Encoding and Decoding Data in Go

Encoding and Decoding Data in Go

Encoding and decoding are essential for data transformation and transmission. Go provides comprehensive support for various encoding formats. This guide covers practical encoding and decoding techniques.

Base64 Encoding

Basic Base64 Operations

package main

import (
	"encoding/base64"
	"fmt"
)

func main() {
	// Encode to base64
	data := "Hello, World!"
	encoded := base64.StdEncoding.EncodeToString([]byte(data))
	fmt.Println("Encoded:", encoded)

	// Decode from base64
	decoded, err := base64.StdEncoding.DecodeString(encoded)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println("Decoded:", string(decoded))
}

URL-Safe Base64

package main

import (
	"encoding/base64"
	"fmt"
)

func main() {
	data := "Hello, World!"

	// URL-safe encoding
	encoded := base64.URLEncoding.EncodeToString([]byte(data))
	fmt.Println("URL-safe encoded:", encoded)

	// URL-safe decoding
	decoded, _ := base64.URLEncoding.DecodeString(encoded)
	fmt.Println("Decoded:", string(decoded))
}

Hex Encoding

Hex Operations

package main

import (
	"encoding/hex"
	"fmt"
)

func main() {
	// Encode to hex
	data := "Hello, World!"
	encoded := hex.EncodeToString([]byte(data))
	fmt.Println("Hex encoded:", encoded)

	// Decode from hex
	decoded, err := hex.DecodeString(encoded)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println("Decoded:", string(decoded))
}

JSON Encoding

JSON Marshaling

package main

import (
	"encoding/json"
	"fmt"
)

type User struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Email string `json:"email"`
}

func main() {
	user := User{ID: 1, Name: "Alice", Email: "[email protected]"}

	// Marshal to JSON
	data, err := json.Marshal(user)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println("JSON:", string(data))

	// Unmarshal from JSON
	var decoded User
	err = json.Unmarshal(data, &decoded)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Printf("Decoded: %+v\n", decoded)
}

Pretty JSON

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	user := map[string]interface{}{
		"id":    1,
		"name":  "Alice",
		"email": "[email protected]",
	}

	// Pretty print JSON
	data, _ := json.MarshalIndent(user, "", "  ")
	fmt.Println(string(data))
}

Custom Encoding

Custom Marshaler

package main

import (
	"encoding/json"
	"fmt"
	"time"
)

type Event struct {
	Name      string
	Timestamp time.Time
}

// Custom JSON marshaling
func (e Event) MarshalJSON() ([]byte, error) {
	type Alias Event
	return json.Marshal(&struct {
		Timestamp string `json:"timestamp"`
		*Alias
	}{
		Timestamp: e.Timestamp.Format(time.RFC3339),
		Alias:     (*Alias)(&e),
	})
}

// Custom JSON unmarshaling
func (e *Event) UnmarshalJSON(data []byte) error {
	type Alias Event
	aux := &struct {
		Timestamp string `json:"timestamp"`
		*Alias
	}{
		Alias: (*Alias)(e),
	}

	if err := json.Unmarshal(data, &aux); err != nil {
		return err
	}

	t, err := time.Parse(time.RFC3339, aux.Timestamp)
	if err != nil {
		return err
	}

	e.Timestamp = t
	return nil
}

func main() {
	event := Event{
		Name:      "Conference",
		Timestamp: time.Now(),
	}

	// Marshal
	data, _ := json.MarshalIndent(event, "", "  ")
	fmt.Println("Encoded:", string(data))

	// Unmarshal
	var decoded Event
	json.Unmarshal(data, &decoded)
	fmt.Printf("Decoded: %+v\n", decoded)
}

Binary Encoding

Binary Serialization

package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
)

func main() {
	// Encode integers
	var buf bytes.Buffer

	// Write int32
	binary.Write(&buf, binary.LittleEndian, int32(42))

	// Write float64
	binary.Write(&buf, binary.LittleEndian, 3.14)

	fmt.Println("Encoded bytes:", buf.Bytes())

	// Decode
	var num int32
	var pi float64

	binary.Read(&buf, binary.LittleEndian, &num)
	binary.Read(&buf, binary.LittleEndian, &pi)

	fmt.Printf("Number: %d, Pi: %f\n", num, pi)
}

Practical Examples

Data Compression

package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io"
)

func compressData(data []byte) ([]byte, error) {
	var buf bytes.Buffer
	writer := gzip.NewWriter(&buf)

	_, err := writer.Write(data)
	if err != nil {
		return nil, err
	}

	writer.Close()
	return buf.Bytes(), nil
}

func decompressData(data []byte) ([]byte, error) {
	reader, err := gzip.NewReader(bytes.NewReader(data))
	if err != nil {
		return nil, err
	}
	defer reader.Close()

	return io.ReadAll(reader)
}

func main() {
	original := []byte("Hello, World! This is a test message.")

	// Compress
	compressed, _ := compressData(original)
	fmt.Printf("Original size: %d, Compressed size: %d\n", len(original), len(compressed))

	// Decompress
	decompressed, _ := decompressData(compressed)
	fmt.Println("Decompressed:", string(decompressed))
}

Protocol Buffer-like Encoding

package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
)

type Message struct {
	ID    uint32
	Name  string
	Score float32
}

func (m *Message) Encode() ([]byte, error) {
	var buf bytes.Buffer

	// Write ID
	binary.Write(&buf, binary.LittleEndian, m.ID)

	// Write name length and data
	binary.Write(&buf, binary.LittleEndian, uint32(len(m.Name)))
	buf.WriteString(m.Name)

	// Write score
	binary.Write(&buf, binary.LittleEndian, m.Score)

	return buf.Bytes(), nil
}

func (m *Message) Decode(data []byte) error {
	buf := bytes.NewReader(data)

	// Read ID
	binary.Read(buf, binary.LittleEndian, &m.ID)

	// Read name
	var nameLen uint32
	binary.Read(buf, binary.LittleEndian, &nameLen)
	nameBytes := make([]byte, nameLen)
	buf.Read(nameBytes)
	m.Name = string(nameBytes)

	// Read score
	binary.Read(buf, binary.LittleEndian, &m.Score)

	return nil
}

func main() {
	msg := Message{ID: 1, Name: "Alice", Score: 95.5}

	// Encode
	encoded, _ := msg.Encode()
	fmt.Println("Encoded:", encoded)

	// Decode
	var decoded Message
	decoded.Decode(encoded)
	fmt.Printf("Decoded: %+v\n", decoded)
}

CSV Encoding

package main

import (
	"bytes"
	"encoding/csv"
	"fmt"
)

type Record struct {
	Name  string
	Age   int
	Email string
}

func encodeCSV(records []Record) (string, error) {
	var buf bytes.Buffer
	writer := csv.NewWriter(&buf)

	// Write header
	writer.Write([]string{"Name", "Age", "Email"})

	// Write records
	for _, r := range records {
		writer.Write([]string{r.Name, fmt.Sprintf("%d", r.Age), r.Email})
	}

	writer.Flush()
	return buf.String(), nil
}

func main() {
	records := []Record{
		{"Alice", 30, "[email protected]"},
		{"Bob", 25, "[email protected]"},
	}

	csv, _ := encodeCSV(records)
	fmt.Println(csv)
}

Best Practices

โœ… Good Practices

// Use appropriate encoding for use case
encoded := base64.StdEncoding.EncodeToString(data)

// Handle errors properly
decoded, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
	// Handle error
}

// Use custom marshalers for complex types
func (t *Type) MarshalJSON() ([]byte, error) {
	// Custom logic
}

// Use binary encoding for performance
binary.Write(&buf, binary.LittleEndian, value)

// Validate decoded data
if err := json.Unmarshal(data, &obj); err != nil {
	// Handle error
}

โŒ Anti-Patterns

// Don't ignore encoding errors
encoded, _ := json.Marshal(data)

// Don't use wrong encoding for use case
// Use URL-safe base64 for URLs

// Don't hardcode byte order
// Use binary.LittleEndian or binary.BigEndian

// Don't assume data format
// Always validate decoded data

Resources

Summary

Encoding and decoding are essential for data transformation:

  • Use base64 for text-safe encoding
  • Use hex for debugging and display
  • Use JSON for structured data
  • Use binary for performance
  • Handle errors properly
  • Validate decoded data
  • Use custom marshalers for complex types

With these techniques, you can efficiently encode and decode data in Go applications.

Comments