Go言語でWEB APIからのJSON用構造体を楽に作る

## はじめに
Qiitaでストックした記事へコマンドラインからアクセスするCLIアプリをGoで作りました。
> https://github.com/momotaro98/qiic


このアプリを作る際、ユーザがストックしている記事を取得するAPI利用したので、そのときに使ったツールと簡単な実装を示します。

## APIから得られるJSONの確認
利用したAPIからは以下のようなJSONデータが得られます([Qiita API v1 Document](https://qiita.com/api/v1/docs#%E7%89%B9%E5%AE%9A%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%81%AE%E3%82%B9%E3%83%88%E3%83%83%E3%82%AF%E3%81%97%E3%81%9F%E6%8A%95%E7%A8%BF%E5%8F%96%E5%BE%97)より)


```json
[{"id": 1,
 "uuid": "1a43e55e7209c8f3c565",
 "user":
  {"name": "Hiroshige Umino",
   "url_name": "yaotti",
   "profile_image_url": "https://si0.twimg.com/profile_images/2309761038/1ijg13pfs0dg84sk2y0h_normal" },
 "title": "てすと",
 "body": "<p>foooooooooooooooo</p>\n",
 "created_at": "2012-10-03 22:12:36 +0900",
 "updated_at": "2012-10-03 22:12:36 +0900",
 "created_at_in_words": "18 hours ago",
 "updated_at_in_words": "18 hours ago",
 "tags":
  [{"name": "FOOBAR",
    "url_name": "FOOBAR",
    "icon_url": "http://qiita.com/icons/thumb/missing.png",
    "versions": ["1.2", "1.3"]}],
 "stock_count": 0,
 "stock_users": [],
 "comment_count": 0,
 "url": "http://qiita.com/items/1a43e55e7209c8f3c565",
 "gist_url": null,
 "tweet": false,
 "private": false,
 "stocked": false
},
 ...
]

```

これらのデータ項目を必要分、構造体で`Unmarshal`して受取りたいのですが、対応する構造体を書くのは *めんどい* です。

## JSON-to-Go

> [JSON-to-Go](https://mholt.github.io/json-to-go/)

↑やはり、便利なものを作ってくれている人たちがいました。

WEB画面に上記のJSONデータを入れると以下のように変換してくれます。

![json-to-go.jpg](https://qiita-image-store.s3.amazonaws.com/0/91562/b37dcc05-acde-a70c-3fa9-526e349204c3.jpeg "json-to-go.jpg")

コピペし、`AutoGenerated`を適切な名前に変え、あとは以下のように良しなに書きます。

## 実装

#### サンプルコード
```go
package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

// コピペした構造体
type Article struct {
	ID   int    `json:"id"`
	UUID string `json:"uuid"`
	User struct {
		Name            string `json:"name"`
		URLName         string `json:"url_name"`
		ProfileImageURL string `json:"profile_image_url"`
	} `json:"user"`
	Title string `json:"title"`
	// Body             string `json:"body"` // 不要な項目
	CreatedAt        string `json:"created_at"`
	UpdatedAt        string `json:"updated_at"`
	CreatedAtInWords string `json:"created_at_in_words"`
	UpdatedAtInWords string `json:"updated_at_in_words"`
	Tags             []struct {
		Name     string   `json:"name"`
		URLName  string   `json:"url_name"`
		IconURL  string   `json:"icon_url"`
		Versions []string `json:"versions"`
	} `json:"tags"`
	StockCount int `json:"stock_count"`
	// StockUsers   []interface{} `json:"stock_users"` // 不要な項目
	CommentCount int    `json:"comment_count"`
	URL          string `json:"url"`
	// GistURL      interface{}   `json:"gist_url"`  // 不要な項目
	Tweet   bool `json:"tweet"`
	Private bool `json:"private"`
	Stocked bool `json:"stocked"`
}

func fetch() ([]Article, error) {
	// APIを叩く
	res, err := http.Get("https://qiita.com/api/v1/users/momotaro98/stocks")
	if err != nil {
		return nil, err
	} else if res.StatusCode != 200 {
		return nil, fmt.Errorf("Unable to get this url : http status %d", res.StatusCode)
	}
	defer res.Body.Close()

	// jsonを読み込む
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	// jsonを構造体へデコード
	var articles []Article
	if err := json.Unmarshal(body, &articles); err != nil {
		return nil, err
	}
	return articles, nil
}

func main() {
	articles, err := fetch()
	if err != nil {
		log.Fatalf("Error!: %v", err)
	}
	for _, a := range articles {
		// 記事タイトルを表示
		fmt.Println(a.Title)
	}
}

```

#### 出力
```shell-session
loggingについて話そう
ttygifでターミナル操作ログのgifアニメ化
Go のクロスコンパイル環境構築
GitHubのリリース機能を使う
Golang Goの並列処理を学ぶ(goroutine, channel)
Goルーチンで並行化する方法: 6秒かかる処理を3秒にしよう
【意訳】Webpackの混乱ポイント
JavaScript の ジェネレータ を極める！
JSおくのほそ道 #000 [IndexPage]
質問は恥ではないし役に立つ
機械学習をゼロから1ヵ月間勉強し続けた結果
これだけは覚えたい、ユニットテストを書くための４つのデザイン
Xamarinを採用するメリット・デメリット
ペアプログラミングして気がついた新人プログラマの成長を阻害する悪習
不安とストレスから解放される見積りとスケジュール方法
Python3で文字列を処理する際の心掛け
Goのjson.Unmarshalで値にJSONのnumber、string両方の可能性がある場合
社会人になってからプログラムを始める人へ
春からはじめるモダンJavaScript / ES2015
go test で出来ること
```

ストックした記事を取得できました!