4 redditnews.go (Second Iteration)
As seen before, the Reddit API returns JSON data like this:
{"data":
{"children": [
{"data": {
"title": "The Go homepage",
"url": "http://golang.org",
...
}},
...
]}}
Go’s json package (encoding/json) decodes JSON-encoded data into native Go data structures. To decode the API response, declare some types that reflect the structure of the JSON data:
type Item struct {
Author string `json:"author"`
Score int `json:"score"`
URL string `json:"url"`
Title string `json:"title"`
}
type response struct {
Data1 struct {
Children []struct {
Data2 Item `json:"data"`
} `json:"children"`
} `json:"data"`
}
We are using tags on struct field declarations to customize the encoded JSON key names. The fields use the json: tag to specify which field they map to.
In Go, top-level declarations beginning with an uppercase letter are “exported” (visible outside the package) and with a lowercase are “unexported” (it can’t be directly accessed from any other package). We have kept response as lowercase. Read William Kennedy’s blog post on Exported/Unexported Identifiers In Go.
Now let’s modify our existing main() function to Decode the response into our Data Structure, instead of copying it to os.Stdout.
Replace:
_, err = io.Copy(os.Stdout, resp.Body)
if err != nil {
log.Fatal(err)
}
With:
r := new(response)
err = json.NewDecoder(resp.Body).Decode(r)
What we’re doing here is initialising a new response value, and storing a pointer to it in our newly assigned variable r. Then create a new json.Decoder object and decode the response body into r.
We should use json.Decoder if the data is coming from an io.Reader stream (for the case of reading from an HTTP request we are obviously reading from a stream), or if we need to decode multiple values from a stream of data.
As the decoder parses the JSON data it looks for corresponding fields of the same names in the response struct. The “data” field of the top-level JSON object is decoded into the response struct’s Data1 field, and JSON array children are decoded into Children slice, and so on.
We need to now print the data. To do this, we are going to create a FOR loop to iterate over the Children slice, assigning the slice value to “child” on each iteration. Then we’re going to print item’s Author, Score, URL and Title followed by a new line:
for _, child := range r.Data1.Children {
fmt.Println(child.Data2.Author)
fmt.Println(child.Data2.Score)
fmt.Println(child.Data2.URL)
fmt.Println(child.Data2.Title)
}
You could use log.Println instead of fmt.Println.
Also note that we need to remove the following imports io, os and add the following encoding/json, net/http to the program that follows.
The complete program so far:
Program redditnews.go
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
type Item struct {
Author string `json:"author"`
Score int `json:"score"`
URL string `json:"url"`
Title string `json:"title"`
}
type response struct {
Data1 struct {
Children []struct {
Data2 Item `json:"data"`
} `json:"children"`
} `json:"data"`
}
func main() {
resp, err := http.Get("http://reddit.com/r/golang.json")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Fatal(resp.Status)
}
r := new(response)
err = json.NewDecoder(resp.Body).Decode(r)
if err != nil {
log.Fatal(err)
}
for _, child := range r.Data1.Children {
fmt.Println(child.Data2.Author)
fmt.Println(child.Data2.Score)
fmt.Println(child.Data2.URL)
fmt.Println(child.Data2.Title)
}
}
You can download this code here.
To work with some printing functions, we import the package fmt above.
Now you can run the program with the go tool:
$ cd $GOPATH/src/github.com/SatishTalim/redditnews
$ go run redditnews.go
Here is a sample output:
natefinch
26
http://blog.natefinch.com/2014/05/intro-to-go-interfaces.html
Intro++ to Go Interfaces
Feribg
19
http://www.techtalkshub.com/go-circuit-towards-elastic-computation-failures/
The Go Circuit: Towards Elastic Computation with No Failures