Golang – Read JSON File

JSON (Javascript Object Notation) is a widely used format for representing data. Due to its easy-to-read format, developers often use JSON files to represent important configuration/environment variables.

Apart from this, most RESTful APIs return data encoded in a JSON format, so knowing how to deal with such data can be very useful in real world applications as well.

In this tutorial, we’ll be looking at ways to extract information from JSON files, using Go!

Reading structured data from JSON Files

In our previous tutorial, we saw how we could deal with sample JSON data using the encoding/json package in the standard library.

We can extend this approach and use the same encoders / decoders on a JSON file. However, since we’ll need to read from a file, we can use the io/ioutil package too.

Let’s assume a simple file config.json, having the following content:

{
    "origin": "golangdocs.com",
    "user": "Vijay",
    "active": true
}

Let’s now extract the JSON data from this config.json file.

// main.go
package main

import (
    "encoding/json"
    "io/ioutil"
    "log"
)

// The data struct for the decoded data
// Notice that all fields must be exportable!
type Data struct {
    Origin string
    User   string
    Active bool
}

func main() {
    // Let's first read the `config.json` file
    content, err := ioutil.ReadFile("./config.json")
    if err != nil {
        log.Fatal("Error when opening file: ", err)
    }

    // Now let's unmarshall the data into `payload`
    var payload Data
    err = json.Unmarshal(content, &payload)
    if err != nil {
        log.Fatal("Error during Unmarshal(): ", err)
    }

    // Let's print the unmarshalled data!
    log.Printf("origin: %s\n", payload.Origin)
    log.Printf("user: %s\n", payload.User)
    log.Printf("status: %t\n", payload.Active)
}

You should get the below output, after creating config.json.

Sample Output

2021/02/07 16:53:53 origin: golangdocs.com
2021/02/07 16:53:53 user: Vijay
2021/02/07 16:53:53 status: true

Seems straightforward, but you must be a bit careful about one thing, if you’re a newcomer to go.

Golang has a concept of “exported” fields in struct datatypes and functions, so this essentially means that these exported fields are the ones which will be visible across different packages. Since the json unmarshal function is external, it can only see exportable fields.

To make a field exportable, just ensure that you capitalize it! That’s why it’s Origin, User and Active, as opposed to origin, user and active.

Reading Unstructured Data from JSON Files

If the contents of our config.json file keep changing regularly, it is practically impossible to keep track of the changes by modifying the struct fields again and again.

To simplify this, we can use the concept of encoding arbitrary data as an interface. We can replicate the JSON structure by visualizing it as a key-value map.

Here, the key will be a string, and the value can be any interface{}.

So we can simply modify the Data struct into a map[string]interface{}!

// main.go
package main

import (
    "encoding/json"
    "io/ioutil"
    "log"
)

func main() {
    // Let's first read the `config.json` file
    content, err := ioutil.ReadFile("./config.json")
    if err != nil {
        log.Fatal("Error when opening file: ", err)
    }

    // Now let's unmarshall the data into `payload`
    var payload map[string]interface{}
    err = json.Unmarshal(content, &payload)
    if err != nil {
        log.Fatal("Error during Unmarshal(): ", err)
    }

    // Let's print the unmarshalled data!
    log.Printf("origin: %s\n", payload["origin"])
    log.Printf("user: %s\n", payload["user"])
    log.Printf("status: %t\n", payload["active"])
}

Notice now that we can store arbitrary data easily, due to the interface{} map!

Conclusion

Hopefully, you’ve now got a good understanding of how you can read JSON data using Go.