Advanced

JSON and Structs

Marshal and unmarshal JSON using struct tags.

What you will see

age is missing

About 1 min of reading

Try It

You will decode JSON into a struct, marshal a struct back to JSON, see how struct tags rename fields, and observe how missing keys are handled.

1

I decode a JSON object into a struct field by field. The field names match case-insensitively, so the name lands in the right place.

package main import ( "encoding/json" "fmt" ) type Person struct { Name string Age int } func main() { data := []byte(`{"name":"Ada","age":30}`) var p Person _ = json.Unmarshal(data, &p) fmt.Println(p.Name) }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Ada
2

I marshal a struct into JSON bytes and print them as a string. Field names appear exactly as declared in the struct.

package main import ( "encoding/json" "fmt" ) type Person struct { Name string Age int } func main() { p := Person{Name: "Ada", Age: 30} out, _ := json.Marshal(p) fmt.Println(string(out)) }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
{"Name":"Ada","Age":30}
3

I add struct tags to rename the JSON keys. Marshal now uses the tag names, so the output shows full_name and years instead of the Go field names.

package main import ( "encoding/json" "fmt" ) type Person struct { Name string `json:"full_name"` Age int `json:"years"` } func main() { p := Person{Name: "Ada", Age: 30} out, _ := json.Marshal(p) fmt.Println(string(out)) }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
{"full_name":"Ada","years":30}
Tags use backticks so the tag text is a raw string; this keeps the double quotes inside the tag easy to read.
4

I decode JSON that omits a field. The missing key leaves the struct field at its zero value, so the age prints as 0.

package main import ( "encoding/json" "fmt" ) type Person struct { Name string Age int } func main() { data := []byte(`{"name":"Ada"}`) var p Person _ = json.Unmarshal(data, &p) fmt.Println(p.Name, p.Age) }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Ada 0
5

I use a pointer field to detect a missing key. When the key is absent, the pointer stays nil and I can report that the value is missing.

package main import ( "encoding/json" "fmt" ) type Person struct { Name string Age *int } func main() { data := []byte(`{"name":"Ada"}`) var p Person _ = json.Unmarshal(data, &p) if p.Age == nil { fmt.Println("age is missing") } }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
age is missing
A non-pointer field only ever shows a zero value, which can be ambiguous. A pointer lets you distinguish 'absent' from 'present as zero'.

Data Flow

JSON bytes are decoded into a struct or encoded back to bytes.
1 Receive JSON bytes
↓
2 Unmarshal maps fields into the struct
↓
3 Or Marshal reads struct fields
↓
4 Encode into JSON text
Why this works
Struct tags for JSON

Tags control JSON field names:

type Config struct {
    APIKey string `json:"api_key"`
    Debug  bool   `json:"debug"`
}

json:"api_key" maps the Go field APIKey to the JSON key api_key. Use - to skip a field.

Marshal and unmarshal

The pair works together:

bytes, _ := json.Marshal(v)     // struct -> JSON
var v2 T
json.Unmarshal(bytes, &v2)      // JSON -> struct

Always handle the returned error, malformed JSON fails decoding. Use Indent for readable output.

Examples

Unmarshaling JSON

Reading JSON

data := []byte(`{"name":"Ada","age":30}`)
var p Person
json.Unmarshal(data, &p)
fmt.Println(p.Name)

json.Unmarshal decodes JSON into a struct. Field names match case-insensitively by default.

Why it works: the struct fields define where each JSON value goes.

Marshaling JSON

Writing JSON

out, _ := json.Marshal(p)
fmt.Println(string(out))   // {"Name":"Ada","Age":30}

json.Marshal turns a struct into JSON bytes. Use struct tags to rename fields:

Person struct {
    Name string `json:"full_name"`
}

Handling missing fields

Robust decoding

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

Missing JSON fields leave the struct field at its zero value. Use a pointer field to detect absence, nil means the key was not present.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Goroutines and Concurrency
Previous Lesson
Goroutines and Concurrency
Next Lesson
Modules and Packages
Modules and Packages →