Parsing JSON files With Golang

Elliot Forbes Elliot Forbes ⏰ 7 Minutes 📅 Apr 9, 2017

Welcome all, in this tutorial, we are going to be taking a look at how you can read in JSON files, or JSON HTTP responses and parse them to your hearts desire.

JSON or Javascript Object Notation as it is short for, is a standard format for sending and receiving information. We could represent the same information with either XML or JSON, but JSON provides one advantage in the fact it is far more compact and in my personal experience, more readable.

JSON is now the most popular data format available and you’ll find that most RESTful APIs provide JSON responses when you try to interface with them. Thus being able to work with it and parse it in Go is incredibly useful!

The Encoding/Json Package

So, to get us started, we’ll be leveraging the encoding/json standard library package in order to get us up and running. I highly recommend you check out the official documentation for this here: Encoding/Json.

Let’s start with a really simple Go program as our base, we’ll build this out to showcase how to work with various different examples. Create a new file called main.go.

main.go
package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello World")
}

And we can run this with a simple go run main.go call. This should return a simple Hello World.

Reading and Parsing a JSON File

Let’s try and read in a simple JSON file and then parse it. For the purpose of this tutorial we’ll be parsing the following json within our file. Copy this and save it into a users.json file within the same directory as your main.go file.

users.json
{
  "users": [
    {
      "name": "Elliot",
      "type": "Reader",
      "age": 23,
      "social": {
        "facebook": "https://facebook.com",
        "twitter": "https://twitter.com"
      }
    },
    {
      "name": "Fraser",
      "type": "Author",
      "age": 17,
      "social": {
        "facebook": "https://facebook.com",
        "twitter": "https://twitter.com"
      }
    }
  ]
}

This should be complex enough to test our skills and should allow us to transfer our skills to real world examples fairly easily.

Reading the JSON File

We’ll be using the os package in order to open up our users.json file from our filesystem. Once we have opened the file, we’ll defer the closing of the file till the end of the function so that we can work with the data inside of it.

// Open our jsonFile
jsonFile, err := os.Open("users.json")
// if we os.Open returns an error then handle it
if err != nil {
    fmt.Println(err)
}
fmt.Println("Successfully Opened users.json")
// defer the closing of our jsonFile so that we can parse it later on
defer jsonFile.Close()

Parsing with Structs

We have a few options when it comes to parsing the JSON that is contained within our users.json file. We could either unmarshal the JSON using a set of predefined structs, or we could unmarshal the JSON using a map[string]interface{} to parse our JSON into strings mapped against arbitrary data types.

If you know the structure that you are expecting then I would recommend going down the verbose route and defining your structs like so:

main.go
package main

import (
    
    // import our encoding/json package
    encoding/json
    
)

// Users struct which contains
// an array of users
type Users struct {
    Users []User `json:"users"`
}

// User struct which contains a name
// a type and a list of social links
type User struct {
    Name   string `json:"name"`
    Type   string `json:"type"`
    Age    int    `json:"Age"`
    Social Social `json:"social"`
}

// Social struct which contains a
// list of links
type Social struct {
    Facebook string `json:"facebook"`
    Twitter  string `json:"twitter"`
}

Once we have these in place, we can use them to unmarshal our JSON.

Unmarshalling our JSON

Once we’ve used the os.Open function to read our file into memory, we then have to convert it toa byte array using ioutil.ReadAll. Once it’s in a byte array we can pass it to our json.Unmarshal() method.

// read our opened jsonFile as a byte array.
byteValue, _ := ioutil.ReadAll(jsonFile)

// we initialize our Users array
var users Users

// we unmarshal our byteArray which contains our
// jsonFile's content into 'users' which we defined above
json.Unmarshal(byteValue, &users)

// we iterate through every user within our users array and
// print out the user Type, their name, and their facebook url
// as just an example
for i := 0; i < len(users.Users); i++ {
    fmt.Println("User Type: " + users.Users[i].Type)
    fmt.Println("User Age: " + strconv.Itoa(users.Users[i].Age))
    fmt.Println("User Name: " + users.Users[i].Name)
    fmt.Println("Facebook Url: " + users.Users[i].Social.Facebook)
}

Working with Unstructured Data

Sometimes, going through the process of creating structs for everything can be somewhat time consuming and overly verbose for the problems you are trying to solve. In this instance, we can use standard interfaces{} in order to read in any JSON data:

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "os"
)

func main() {

    // Open our jsonFile
    jsonFile, err := os.Open("users.json")
    // if we os.Open returns an error then handle it
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println("Successfully Opened users.json")
    // defer the closing of our jsonFile so that we can parse it later on
    defer jsonFile.Close()

    byteValue, _ := ioutil.ReadAll(jsonFile)

    var result map[string]interface{}
    json.Unmarshal([]byte(byteValue), &result)

    fmt.Println(result["users"])

}

You can see in the above code, we’ve managed to open our users.json and parse the JSON much like we would normally do in other programming languages such as Python or JavaScript.

When we run this, we should see that printing result["users"] results in a map being outputted to the console:

$ go run main.go
Successfully opened users.json
[map[type:Reader age:23 social:map[facebook:https://facebook.com twitter:https://twitter.com] name:Elliot] map[name:Frasertype:Author age:17 social:map[facebook:https://facebook.com twitter:https://twitter.com]]]

If we wanted to traverse further down the tree, we could do that just as we normally would traverse down a map structure within Go, without having to define the struct types.

Note - It is typically recommended to try and define the structs, if you happen to know the structure of the data coming back.

Full Implementation

Below you’ll find the full implementation of this tutorial.

main.go
package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "os"
    "strconv"
)

// Users struct which contains
// an array of users
type Users struct {
    Users []User `json:"users"`
}

// User struct which contains a name
// a type and a list of social links
type User struct {
    Name   string `json:"name"`
    Type   string `json:"type"`
    Age    int    `json:"Age"`
    Social Social `json:"social"`
}

// Social struct which contains a
// list of links
type Social struct {
    Facebook string `json:"facebook"`
    Twitter  string `json:"twitter"`
}

func main() {
    // Open our jsonFile
    jsonFile, err := os.Open("users.json")
    // if we os.Open returns an error then handle it
    if err != nil {
        fmt.Println(err)
    }

    fmt.Println("Successfully Opened users.json")
    // defer the closing of our jsonFile so that we can parse it later on
    defer jsonFile.Close()

    // read our opened xmlFile as a byte array.
    byteValue, _ := ioutil.ReadAll(jsonFile)

    // we initialize our Users array
    var users Users

    // we unmarshal our byteArray which contains our
    // jsonFile's content into 'users' which we defined above
    json.Unmarshal(byteValue, &users)

    // we iterate through every user within our users array and
    // print out the user Type, their name, and their facebook url
    // as just an example
    for i := 0; i < len(users.Users); i++ {
        fmt.Println("User Type: " + users.Users[i].Type)
        fmt.Println("User Age: " + strconv.Itoa(users.Users[i].Age))
        fmt.Println("User Name: " + users.Users[i].Name)
        fmt.Println("Facebook Url: " + users.Users[i].Social.Facebook)
    }

}

Conclusion

Hopefully this tutorial helped to demystify the art of working with JSON in Golang. If you found this tutorial helpful or have anything else to add then please let me know in the comments section below.

Further Reading

If you enjoyed this tutorial or found it useful, you may also enjoy some of my other articles on the site: