DEV Community

Abhishek Gupta for ITNEXT

Posted on • Updated on

How to work with JSON data in Go

Welcome to Just Enough Go ! This is the first in a series of articles about the Go programming language in which I will be covering some of the most commonly used Go standard library packages e.g. encoding/json, io, net/http, sync etc. I plan to keep these relatively short and example driven.

the code is available in this GitHub repo

I would be more than happy to take suggestions on specific Go topics which you would like to me cover! Feel free to tweet or just drop a comment ๐Ÿ˜ƒ

This article covers the encoding/json package which handles conversion between JSON and Go types (as per RFC 7159). Let's jump right in!

Converting a Go type into JSON

Marshal

A common option is to use the Marshal function whose signature is:

    func Marshal(v interface{}) ([]byte, error)
Enter fullscreen mode Exit fullscreen mode

Here is an example:

func main() {
    profile := Profile{Email: "abhirockzz@gmail.com", Username: "abhirockzz", Blogs: []Blog{
        Blog{BlogName: "devto", URL: "https://dev.to/abhirockzz/"},
        Blog{BlogName: "medium", URL: "https://medium.com/@abhishek1987/"},
    }}
    jsonData, err := json.Marshal(&myprofile)
    //jsonData, err := json.MarshalIndent(&profile, "", " ")
    if err != nil {
        panic(err)
    }
    fmt.Println(string(jsonData))
}
Enter fullscreen mode Exit fullscreen mode

you can use MarshalIndent (commented) to indent the JSON output

Encoder

While Marshal deals in byte arrays ([]byte), an Encoder is generic and lets you work with an io.Writer which you can define to be the sink for JSON data i.e. you can specify any type which implements the io.Writer interface e.g. this could be standard out (os.Stdout), or an HTTP response (http.ResponseWriter), etc.

func main() {
    profile := Profile{Email: "abhirockzz@gmail.com", Username: "abhirockzz", Blogs: []Blog{
        Blog{BlogName: "devto", URL: "https://dev.to/abhirockzz/"},
        Blog{BlogName: "medium", URL: "https://medium.com/@abhishek1987/"},
    }}

    encoder := json.NewEncoder(os.Stdout)
    err := encoder.Encode(&profile)
    if err != nil {
        panic(err)
    }
}
Enter fullscreen mode Exit fullscreen mode

Use NewEncoder to specify the io.Writer. When you call Encode, the conversion takes place and the JSON is writer to io.Writer you specified.

Here is an example of how it would work with an HTTP response:

func main() {
    profile := Profile{Email: "abhirockzz@gmail.com", Username: "abhirockzz", Blogs: []Blog{
        Blog{BlogName: "devto", URL: "https://dev.to/abhirockzz/"},
        Blog{BlogName: "medium", URL: "https://medium.com/@abhishek1987/"},
    }}

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        encoder := json.NewEncoder(w)
        err := encoder.Encode(&profile)
        if err != nil {
            panic(err)
        }
    })

    http.ListenAndServe(":8080", nil)
}
Enter fullscreen mode Exit fullscreen mode

Converting JSON data to a Go type

Unmarshal

Given JSON in form of a byte array, Unmarshal stores the JSON parsing result to a pointer of the Go data type you specify (typically a struct)

func Unmarshal(data []byte, v interface{}) error
Enter fullscreen mode Exit fullscreen mode

Here is a simple example:

func main() {
    jsonData := `{"email":"abhirockzz@gmail.com","username":"abhirockzz","blogs":[{"name":"devto","url":"https://dev.to/abhirockzz/"},{"name":"medium","url":"https://medium.com/@abhishek1987/"}]}`

    var profile Profile
    err := json.Unmarshal([]byte(jsonData), &profile)
    if err != nil {
        panic(err)
    }
    ...
}
Enter fullscreen mode Exit fullscreen mode

Decoder

Decoder provides a generic way of unmarshalling JSON data by allowing you to specify the source of JSON input in the form of an io.Reader as opposed to a byte array.

func main() {
    jsonData := `{"email":"abhirockzz@gmail.com","username":"abhirockzz","blogs":[{"name":"devto","url":"https://dev.to/abhirockzz/"},{"name":"medium","url":"https://medium.com/@abhishek1987/"}]}`

    jsonDataReader := strings.NewReader(jsonData)
    decoder := json.NewDecoder(jsonDataReader)

    var profile Profile
    err := decoder.Decode(&profile)
    if err != nil {
        panic(err)
    }
    ...
}
Enter fullscreen mode Exit fullscreen mode

We start by creating an io.Reader from the JSON string data using a shortcut strings.NewReader. We can then instantiate the decoder using NewDecoder and simply use decode with a pointer to the Profile struct to which the result is stored

That's all for this blog! Stay tuned for more and don't forget to like and follow ๐Ÿ˜ƒ๐Ÿ˜ƒ

Top comments (1)

Collapse
 
tehsphinx profile image
Gabriel Nelle • Edited

I think this article is omitting a critical part: The definition of Profile. A reader that doesn't know how to define Profile will not be able to reproduce this. Looking at your github code will probably help, but the key to json handling in Go with structs is in the annotations on the struct -- which is not mentioned here.

Note: Profile could also be a map[string]interface{} (which I'd not recommend to use!!).