Go JSON Encoding and Decoding

JSON (JavaScript Object Notation) is the standard format for exchanging data between systems — web APIs, config files, and services all use it. Go's encoding/json package converts Go structs to JSON text (encoding) and converts JSON text back into Go structs (decoding).

JSON and Go Types Mapping

Go TypeJSON Type
string"text"
int, float6442, 3.14
booltrue / false
[]T (slice)[...] (array)
map[string]T{...} (object)
struct{...} (object)
nilnull

Encoding – Struct to JSON

Use json.Marshal to convert a Go value into JSON bytes.

package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name  string `json:"name"`
    Age   int    `json:"age"`
    Email string `json:"email"`
}

func main() {
    p := Person{
        Name:  "Alice",
        Age:   30,
        Email: "alice@example.com",
    }

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

    fmt.Println(string(data))
}

Output:

{"name":"Alice","age":30,"email":"alice@example.com"}

Struct Tags Explained

type Person struct {
    Name  string `json:"name"`
}
              │         │
              │         └── key used in the JSON output
              └──────────── backtick syntax for struct tags

Without a tag:   field "Name" becomes "Name" in JSON
With tag:        field "Name" becomes "name" in JSON

Pretty-Printed JSON

Use json.MarshalIndent to produce readable, formatted JSON.

package main

import (
    "encoding/json"
    "fmt"
)

type Product struct {
    ID    int     `json:"id"`
    Name  string  `json:"name"`
    Price float64 `json:"price"`
}

func main() {
    p := Product{ID: 1, Name: "Go Book", Price: 29.99}

    data, _ := json.MarshalIndent(p, "", "  ")
    fmt.Println(string(data))
}

Output:

{
  "id": 1,
  "name": "Go Book",
  "price": 29.99
}

Decoding – JSON to Struct

Use json.Unmarshal to convert JSON bytes into a Go struct.

package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name  string `json:"name"`
    Age   int    `json:"age"`
    Email string `json:"email"`
}

func main() {
    jsonData := `{"name":"Bob","age":25,"email":"bob@example.com"}`

    var p Person
    err := json.Unmarshal([]byte(jsonData), &p)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Println(p.Name)  // Bob
    fmt.Println(p.Age)   // 25
    fmt.Println(p.Email) // bob@example.com
}

Encoding and Decoding Flow

ENCODING (Marshal):
Go Struct  ──► json.Marshal()  ──► JSON bytes  ──► string(data)

DECODING (Unmarshal):
JSON string  ──► []byte(s)  ──► json.Unmarshal()  ──► Go Struct

Handling Optional Fields – omitempty

The omitempty option skips a field in the JSON output if the field has its zero value.

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    Name     string `json:"name"`
    Age      int    `json:"age,omitempty"`   // omit if 0
    Nickname string `json:"nickname,omitempty"` // omit if ""
}

func main() {
    u := User{Name: "Carol"}
    data, _ := json.Marshal(u)
    fmt.Println(string(data)) // {"name":"Carol"}
}

Decoding JSON Array

package main

import (
    "encoding/json"
    "fmt"
)

type Item struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

func main() {
    jsonData := `[{"id":1,"name":"Go"},{"id":2,"name":"Python"}]`

    var items []Item
    json.Unmarshal([]byte(jsonData), &items)

    for _, item := range items {
        fmt.Printf("ID: %d, Name: %s\n", item.ID, item.Name)
    }
}

Output:

ID: 1, Name: Go
ID: 2, Name: Python

Key Points

  • Use json.Marshal to convert a Go value to JSON bytes
  • Use json.Unmarshal to parse JSON bytes into a Go struct
  • Struct tags like json:"name" control the key names in the JSON output
  • omitempty skips a field from JSON output when it holds a zero value
  • Use json.MarshalIndent for human-readable formatted JSON
  • Always pass a pointer to json.Unmarshal so it can populate the struct

Leave a Comment