Source: https://github.com/go-monk/json
JSON is a widely used notation for sending and receiving structured information. Go supports JSON encoding and decoding via encoding/json standard library package.
Let's say we want to encode a list of books as JSON. First we define a custom type based on struct
to represent a book:
// books/main.go
type Book struct {
Title string
Authors []string
Year int `json:"published,omitzero"`
}
Encoding
The struct fields that you want to encode as JSON must be exported (capitalized). The string after the Year
field is called a field tag. The published
piece of the field tag specifies the name of the field in JSON instead of the original Year
. The omitzero
option basically omits the field in JSON if it has the zero value for its type.
Now we create some Book
data and encode (marshal) them as indented JSON:
// books/main.go
books := []Book{
{"Lord of the Rings", []string{"J.R.R. Tolkien"}, 0},
{"That Hideous Strength", []string{"C.S. Lewis"}, 1945},
{"The Go Programming Language", []string{"Alan A. A. Donovan", "Brian W. Kernighan"}, 2015},
}
data, err := json.MarshalIndent(books, "", " ")
if err != nil {
log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Println(string(data))
Decoding
To decode JSON to Go data we use the Unmarshal
function:
// books/main.go
var titles []struct{ Title string }
if err := json.Unmarshal(data, &titles); err != nil {
log.Fatalf("JSON unmarshaling failed: %s", err)
}
for _, t := range titles {
fmt.Println(t.Title)
}
Note that we chose to decode only the book titles.
Practical example
Now let's build a practical tool. GitHub exposes lot of JSON data via HTTP endpoints. For example to get information about repositories of a GitHub organization named golang
you visit https://api.github.com/orgs/golang/repos.
We are interested in the following pieces of information about repositories:
// ghrepos/main.go
type Repo struct {
Name string `json:"name"`
CloneUrl string `json:"clone_url"`
Stars int `json:"stargazers_count"`
Topics []string `json:"topics"`
Description string `json:"description"`
}
Next we get the JSON data and decode them to a slice of Repo
s:
// ghrepos/main.go
func getRepos(url string) ([]Repo, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var repos []Repo
if err := json.Unmarshal(data, &repos); err != nil {
return nil, err
}
return repos, nil
}
All that's needed now is to somehow print the data, for example:
// ghrepos/main.go
func printRepos(repos []Repo) {
tw := new(tabwriter.Writer).Init(os.Stdout, 0, 8, 2, ' ', 0)
const format = "%v\t%v\t%v\t%v\n"
fmt.Fprintf(tw, format, "Name", "Description", "Stars", "URL")
fmt.Fprintf(tw, format, "----", "-----------", "-----", "---")
for _, k := range repos {
fmt.Fprintf(tw, format,
truncate(k.Name, 15),
truncate(k.Description, 30),
k.Stars,
k.CloneUrl,
)
}
tw.Flush()
}
This produces output like:
$ go run ./ghrepos/main.go -org golang
Name Description Stars URL
---- ----------- ----- ---
gddo Go Doc Dot Org 1103 https://github.com/golang/gddo.git
lint [mirror] This is a linter f... 3968 https://github.com/golang/lint.git
glog Leveled execution logs for Go 3612 https://github.com/golang/glog.git
<...>