JSON (JavaScript Object Notation) is a highly versatile and vastly popular data format. It is often used for communication between web apps and also as a convenient way to store large amounts of structured application data.

JSON is so useful that almost all modern programming languages provide built-in support for working with it, including Go.

Working With JSON in Go

You can split most of the work you'll do with JSON into two general categories: marshaling and unmarshaling. Wikipedia defines marshaling as:

In computer science, marshaling is the process of transforming the memory representation of an object into a data format suitable for storage or transmission. It is typically used when data must be moved between different parts of a computer program or from one program to another.

In simpler terms, marshaling is the process of converting data stored in a variable into a form that's easier to pass to another program. Unmarshaling is the reverse process: it involves taking data formatted for transportation and converting it to a form that's easier for your program to use.

Using Go, you can marshal native data structures into JSON. And you can perform the reverse action, unmarshaling JSON data into Go variables.

Marshaling to JSON in Go

Go provides the encoding/json package to make it easy for you to work with JSON. This package contains several functions, but the one you'll be using for marshaling is the Marshal function. Marshal has the following function signature:

        func Marshal(v interface{}) ([]byte, error)

This means that Marshal accepts a parameter of any data type, and returns two values: a slice of bytes, and an error. In other words, you call Marshal with a Go value, and it converts it to JSON and returns the JSON equivalent. If it encounters an error during the conversion process, it'll return the error and an empty slice.

Here's a code example that uses Marshal to convert a map to JSON. To run this example, all you need is to create a Go file in your favorite code editor, or use the Go playground:

        package main
 
import (
    "encoding/json"
    "fmt"
)
 
func main() {
    val := map[string]int{
        "john": 25,
        "mary": 19,
        "adam": 5,
    }
 
    res, _ := json.Marshal(val)
    fmt.Println(string(res))
}

If you run that code, it will output the following:

command line output

As mentioned before, you can marshal any type of Go data to JSON, though in real life you'll usually be marshaling structs. Because of this, Go provides a feature called struct tags to let you give Marshal extra instructions for converting your structs.

A struct tag is a string you include in your struct declaration next to the data type of a field. Struct tags let you adjust the way Marshal treats the field the tag belongs to. You can use struct tags to rename a field in the JSON output, or even omit it entirely. Struct tags (that Marshal recognizes) start with the substring "json:".

As an example, Say you have a struct Car that represents some information about a car. Here's the code to create a Car and marshal it to JSON:

        package main
 
import (
    "encoding/json"
    "fmt"
)
 
func main() {
    type Car struct {
        Brand string
        Model string
        Price int
    }
 
    val := Car{Brand: "Mercedes", Model: "Benz", Price: 50000}
    res, _ := json.Marshal(val)
    fmt.Println(string(res))
}

This code produces the output:

command line output

The Brand, Model, and Price fields of Car need to start with uppercase letters, or Marshal won't be able to convert them. This results in the JSON output fields starting with uppercase too. But what if you want the names to start with lowercase in the JSON, or if you want to rename a field entirely? That's where the struct tags come in. Here's an example:

        package main
 
import (
    "encoding/json"
    "fmt"
)
 
func main() {
    type Car struct {
        ID int `json:"-"`
        Brand string `json:"type"`
        Model string `json:"model"`
        Price int `json:"price"`
    }
 
    val := Car{ID: 0, Brand: "Mercedes", Model: "Benz", Price: 50000}
    res, _ := json.Marshal(val)
    fmt.Println(string(res))
}

This code introduces a new ID field that Marshal omits from the JSON output via a struct tag. The code also uses struct tags to rename the other struct fields. Here's the program output:

command line output

As you can see, the part of the struct tag following "json:" becomes the name of the field in Marshal's output. There's one exception: if it's the string "-", Marshal omits that field from the output. You can read more about Marshal and struct tags in the Go documentation.

Unmarshaling From JSON in Go

The encoding/json package provides an unmarshaling function as well, called Unmarshal. It has the following function signature:

        func Unmarshal(data []byte, v interface{}) error
    

Unlike Marshal, Unmarshal doesn't return a value. Instead, it accepts JSON as a slice of bytes in the first argument and then stores the converted data in the object pointed to by its second argument. Unmarshal works with struct tags too, but here, the tags tell Unmarshal which JSON fields match which struct fields.

When unmarshaling in a program, you might fetch data from an API, but here you'll be using dummy data. Here's how you use Unmarshal:

        package main
 
import (
    "encoding/json"
    "fmt"
)
 
func main() {
    type Car struct {
        ID int `json:"-"`
        Brand string `json:"type"`
        Model string `json:"model"`
        Price int `json:"price"`
    }
 
    jsonInput := `{
        "type": "Toyota",
        "model": "Camry",
        "price": 2000
    }`
 
    var jsonOutput Car
    err := json.Unmarshal([]byte(jsonInput), &jsonOutput)
 
    if err != nil {
        fmt.Println("JSON decode error!")
        return
    }
 
    fmt.Println(jsonOutput)
}

This code uses the same Car type as the previous example, and it unmarshals a JSON object into a struct of type Car, then prints the data in the struct. When run, the program produces this output:

command line output

This shows that the dummy JSON data was successfully unmarshaled into the jsonOutput struct.

Go Makes It Easy to Work With JSON

With the encoding/json package, working with JSON in Go is as simple as two function calls: Marshal and Unmarshal. Go also lets you customize the process of marshaling/unmarshaling JSON with struct tags.

Converting data to JSON is a great way of sharing it with another program or process. The format is so universal that JSON is as portable as can be.