Encoding and Serialization

Updated

July 30, 2026

Overview

Go provides standard encoding packages for JSON, XML, and binary data.

JSON

import "encoding/json"

type User struct {
    Name  string `json:"name"`
    Email string `json:"email,omitempty"`
    Age   int    `json:"-"`  // Ignored
}

// Encode
user := User{Name: "Alice", Email: "a@b.com"}
data, _ := json.Marshal(user)
// {"name":"Alice","email":"a@b.com"}

// Decode
var u User
json.Unmarshal(data, &u)

// Pretty print
data, _ := json.MarshalIndent(user, "", "  ")

Streaming JSON

// Encode to writer
encoder := json.NewEncoder(w)
encoder.Encode(user)

// Decode from reader
decoder := json.NewDecoder(r)
decoder.Decode(&user)

XML

import "encoding/xml"

type Person struct {
    XMLName xml.Name `xml:"person"`
    Name    string   `xml:"name"`
    Age     int      `xml:"age,attr"`
}

data, _ := xml.MarshalIndent(person, "", "  ")
xml.Unmarshal(data, &p)

Gob (Go Binary)

import "encoding/gob"

// Encode
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
enc.Encode(data)

// Decode
dec := gob.NewDecoder(&buf)
dec.Decode(&result)

CSV

import "encoding/csv"

// Write
w := csv.NewWriter(file)
w.Write([]string{"a", "b", "c"})
w.Flush()

// Read
r := csv.NewReader(file)
records, _ := r.ReadAll()

Base64

import "encoding/base64"

encoded := base64.StdEncoding.EncodeToString(data)
decoded, _ := base64.StdEncoding.DecodeString(encoded)

Summary

Package Format
encoding/json JSON
encoding/xml XML
encoding/gob Go binary
encoding/csv CSV
encoding/base64 Base64

Worked example

Streaming JSON encode/decode round-trip over an in-memory buffer.

Save as main.go. Then:

go mod init example
go run .
package main

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

type Event struct {
    Type string `json:"type"`
    N    int    `json:"n"`
}

func main() {
    var buf bytes.Buffer
    enc := json.NewEncoder(&buf)
    for i := 1; i <= 3; i++ {
        if err := enc.Encode(Event{Type: "tick", N: i}); err != nil {
            panic(err)
        }
    }

    dec := json.NewDecoder(&buf)
    sum := 0
    for dec.More() {
        var e Event
        if err := dec.Decode(&e); err != nil {
            panic(err)
        }
        sum += e.N
        fmt.Printf("event type=%s n=%d\n", e.Type, e.N)
    }
    fmt.Println("sum:", sum)
}

Expected output:

event type=tick n=1
event type=tick n=2
event type=tick n=3
sum: 6

More examples

Tag-driven JSON omitempty + gob preserving unexported-unrelated fields.

package main

import (
    "bytes"
    "encoding/gob"
    "encoding/json"
    "fmt"
)

type User struct {
    Name  string `json:"name"`
    Email string `json:"email,omitempty"`
    Score int    `json:"score,omitempty"`
}

func main() {
    u := User{Name: "Ada", Score: 0} // Score omitted when 0
    j, _ := json.Marshal(u)
    fmt.Println("json:", string(j))

    var buf bytes.Buffer
    _ = gob.NewEncoder(&buf).Encode(User{Name: "Ada", Score: 10})
    var out User
    _ = gob.NewDecoder(&buf).Decode(&out)
    fmt.Println("gob score:", out.Score)
}

Expected output:

json: {"name":"Ada"}
gob score: 10

Runnable example

Save as main.go. Then:

go mod init example
go run .
package main

import (
    "bytes"
    "encoding/base64"
    "encoding/csv"
    "encoding/gob"
    "encoding/json"
    "encoding/xml"
    "fmt"
    "strings"
)

type User struct {
    Name  string `json:"name" xml:"name"`
    Email string `json:"email,omitempty" xml:"email"`
    Age   int    `json:"-" xml:"-"` // ignored by JSON/XML tags in this demo
}

type PersonXML struct {
    XMLName xml.Name `xml:"person"`
    Name    string   `xml:"name"`
    Age     int      `xml:"age,attr"`
}

func main() {
    u := User{Name: "Alice", Email: "a@b.com", Age: 30}

    // JSON round-trip
    j, err := json.Marshal(u)
    if err != nil {
        panic(err)
    }
    var u2 User
    if err := json.Unmarshal(j, &u2); err != nil {
        panic(err)
    }
    fmt.Println("json:", string(j))
    fmt.Println("json name:", u2.Name, "age zeroed:", u2.Age == 0)

    // XML round-trip
    p := PersonXML{Name: "Bob", Age: 25}
    x, err := xml.Marshal(p)
    if err != nil {
        panic(err)
    }
    var p2 PersonXML
    if err := xml.Unmarshal(x, &p2); err != nil {
        panic(err)
    }
    fmt.Println("xml:", string(x))
    fmt.Println("xml age:", p2.Age)

    // Gob round-trip (Go-native binary)
    var buf bytes.Buffer
    if err := gob.NewEncoder(&buf).Encode(u); err != nil {
        panic(err)
    }
    var u3 User
    if err := gob.NewDecoder(&buf).Decode(&u3); err != nil {
        panic(err)
    }
    fmt.Println("gob age preserved:", u3.Age)

    // CSV
    var csvBuf strings.Builder
    w := csv.NewWriter(&csvBuf)
    _ = w.Write([]string{"name", "email"})
    _ = w.Write([]string{u.Name, u.Email})
    w.Flush()
    records, err := csv.NewReader(strings.NewReader(csvBuf.String())).ReadAll()
    if err != nil {
        panic(err)
    }
    fmt.Println("csv rows:", len(records), "data:", records[1])

    // Base64
    enc := base64.StdEncoding.EncodeToString([]byte("hi"))
    dec, _ := base64.StdEncoding.DecodeString(enc)
    fmt.Println("b64:", enc, "->", string(dec))
}

Expected output:

json: {"name":"Alice","email":"a@b.com"}
json name: Alice age zeroed: true
xml: <person age="25"><name>Bob</name></person>
xml age: 25
gob age preserved: 30
csv rows: 2 data: [Alice a@b.com]
b64: aGk= -> hi

What to notice: json:"-" drops Age from JSON, so it comes back as zero—gob has no tags and preserves the full struct. XML attributes use ,attr. Prefer streaming Encoder/Decoder for large payloads and HTTP bodies.

Try next: Use json.MarshalIndent for pretty output. Stream-decode a JSON array with decoder.Token / decoder.More.