Handling JSON in Go

September 18, 2019 237 views

Go was built with the modern web in mind. Native concurrency as well as packages like “net/http” for client and server interactions are just the tip of the iceburg of all the goodness that’s packed into Go. Of course, if you plan on developing anything for the web, you will without a doubt come across JSON. While encoding and decoding JSON in other languages such as Java can be a pain, Go’s “encoding/json” package provides all you need to handle those pesky JSON strings.

Marshalling JSON

First thing’s first. You want to encode a JSON string. For that, we use the Marshal function.

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

For example, to encode a User we can do

// User Don't forget to annotate exported values
type User struct {
    Username string
    Password string
}

myUser := User{
    Username: "Sebastian",
    Password: "Its@S3cret",
}

b, err := json.Marshal(myUser)

If all goes right, then you should see that

b == []byte(`{"Username": "Sebastian","Password":"Its@S3cret"}`)

However, in the real world, the field names in JSON often do not match up with your declared field names. In this case, you can trail the struct field declarations with a string literal (called tags) in the following format

type User struct {
    Username string `json:"username"`
    Password string `json:"password"`
}

This would produce a JSON string that looks like this:

{"username": "Sebastian","password":"Its@S3cret"}

Additionally, there are a few other options other than renaming the field:

// Use 'omitempty' if you would like to omit the field when empty
Username string `json:"username,omitempty"`

// Use 'string' to force the value to be encoded as a JSON string
Age int `json:"age,string"`

// Use '-' to ignore the field completly
Hidden string `json:"-"`

Of course, you can choose to keep the original field name and still use one of these options. Simply include a comma before the option.

// Keep my field name as 'Password', but omit it when empty
Password string `json:",omitempty"`

If you would like to make your output look a little prettier, you can use the MarshalIndent function

func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)

which will add a prefix and an indent before each new JSON element. So now you know how to encode JSON, but what about decoding?

Unmarshaling

Decoding is done in a similar fashion, using the Unmarshal function.

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

In this case, we simply want to work the other way around. Given

b := []byte(`{"username":"Meredith","password":"notsecure"}`)

you can decode with the following

var myUser User

err := json.Unmarshal(b, &myUser)

and now myUser represents the decoded value b. Hooray!

⚠️ Unmarshal will only decode fields that are valid for the target type. Invalid fields are ignored.


Additional knowledge

The types that Go associates JSON with are as follows:

  • booleans are bool
  • strings are string
  • numbers are float64
  • null is nil

If you would like to check the validity of a JSON string, you can use Valid

func Valid(data []byte) bool

Overall, there is some really nice functionality built in to Go which makes life that much easier. To learn more about this package, I encourage you to read the official package docs.