読者です 読者をやめる 読者になる 読者になる

今川館

都内勤務の地味OLです

はじめてのwebアプリ③ JSONで結果を出力する

この記事のソースコードこちら

JSONで出力する機能を追加する

はじめてのwebアプリのバグを直したところで、今度はJSONで結果を返す機能も追加してみる。

/countにformat=jsonで問い合わせが来たら、結果をJSONで返却する。

ハンドラ関数を分岐

まずハンドラ関数を分岐する。

func WordCountHandler(w http.ResponseWriter, r *http.Request) {
	if r.FormValue("format") == "json" {
		JSONWordCountHandler(w, r)
	} else {
		HTMLWordCountHandler(w, r)
	}
}

func JSONWordCountHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	// ...以下略
}

func HTMLWordCountHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	// ...以下略
}

json.Marshalは渡された構造体の公開フィールドしか出力しないので注意

Goのオブジェクトをjsonのテキストに出力する場合はencoding/jsonモジュールのMarshalを使う。

但し、利用には注意点がある。

小文字で始まるキーでJSONを出力したければタグを指定する

Marshalには構造体を渡すのだが、ここで渡す構造体の中で公開フィールドしか出力してくれない。
つまり、先頭が大文字で始まる名前のフィールドだけが出力対象となる。

最初、わたしは構造体を以下の通り定義したため、結果が"{}"、空のmapで出力されてしまった。

[ダメな例]

type WordCount struct {
	text  string
	count int
}

result, err := json.Marshal(WordCount{text, count})
if err != nil {
	log.Panic(err)
}
w.Write(result)

[ブラウザで問い合わせると…]

f:id:imagawa_yakata:20161219140820p:plain

↑この通り、空のmapが出力されて、NG。

[フィールドの名前を大文字で始めると]

type WordCount struct {
	Text  string
	Count int
}

result, err := json.Marshal(WordCount{text, count})

[ブラウザで問い合わせると…]

f:id:imagawa_yakata:20161219141030p:plain

↑今度はちゃんとデータが出力された、、が、キーが大文字で始まるのはちょっと・・

Goには「タグ」という仕組みがあって、構造体のフィールド宣言の後ろに文字列を書いておくとものによってはよきにはからってくれる。

json.Marshalはタグを見てくれるので、小文字のキーで出力するよう指定する。

[タグ付きでMarshal]

type WordCount struct {
	Text  string `json:"text"`
	Count int    `json:"count"`
}

result, err := json.Marshal(WordCount{text, count})

[ブラウザで問い合わせると…]

f:id:imagawa_yakata:20161219141334p:plain

↑今度は小文字でキーが出力され、期待通りの結果となった。

文字数とバイト数のふたつを返すよう変更

ついでに文字数の他にバイト数も返却するよう変更する。
さっきのMarshalに渡す構造体をちょっといじるだけ。

text := r.FormValue("text")
count := utf8.RuneCountInString(text)
bc := len(text)

// json.Marshalは構造体の公開フィールドしか出力してくれないので注意。
// 小文字でJSONのキーを出力したければタグを指定する。
type WordCount struct {
	Text      string `json:"text"`
	Count     int    `json:"count"`
	ByteCount int    `json:"byte_count"`
}

result, err := json.Marshal(WordCount{text, count, bc})
if err != nil {
	log.Panic(err)
}
w.Write(result)

出力結果にバイト数が追加されるようになった。

f:id:imagawa_yakata:20161219142357p:plain