go言語でJSONと戯れる

本エントリは、Qiita Advent Calendar 2017 ulgeekの19日目です。

# はじめに
現在業務でJSONを受け取る帳票システムを開発しています。
そのシステムはJavaで実装予定なのですが、個人的に興味のあったgo言語で、
どのようにJSONを扱うのか、ふと気になりました。

JSONでデータのやり取りを行うシステムが多い中、
今後go言語で開発するという素敵な機会があるかもしれないと思い、
（今はなくても、私が色々知っていれば、周りの人を洗脳できるかもしれないと期待しつつ...）
今回はgo言語とJSONについて、書いてみようと思います。

# 何をやるか
go言語の標準パッケージであるencoding/jsonで提供されている、
Marshal関数、Unmarshal関数を用いて、ひたすらJSON文字列を操作します。

# 標準パッケージでJSONを扱う
go言語でJSONを扱うには、encoding/jsonという標準パッケージを利用します。
以下のように、JSON文字列を用意します（HIDEとTAIJIは忘れていませんよ...ただ文字列が長くなってしまうので...）。この文字列を操作します。

```go
var xjapanJson string = `
{
	"members": [
		{ "name": "Toshl",   "instrument": "vocal"  },
		{ "name": "PATA",    "instrument": "guitar" },
		{ "name": "HEATH",   "instrument": "bass"   },
		{ "name": "SUGIZO",  "instrument": "guitar" },
		{ "name": "YOSHIKI", "instrument": "drums"  }
	],
	"songs": ["紅", "Silent Jealousy"]
}
`
```

encoding/jsonパッケージには、JSON文字列を簡単に扱うために、2つの関数が用意されています。

## func Unmarshal(data []byte, v interface{}) error
json.Unmarshal関数は、構造体にjsonタグがあれば、対応する変数に値を代入します。
第1引数にJSONのバイト列、第2引数にJSONをマッピングしたい値（今回の場合、構造体）のポインターです。

今回の場合、new()で生成したXJapan構造体のポインターを渡しています。

```go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
)

type Musician struct {
	Name string `json:"name"`
	Instrument string `json:"instrument"`
}

type XJapan struct {
	Members []Musician `json:"members"`
	Songs  []string `json:"songs"`
}

func main() {
	// Unmarshal結果の格納先である構造体のポインターを取得
	xJapan := new(XJapan)
	
    // JSON文字列をバイト列にキャスト
	jsonBytes := []byte(xjapanJson)

    // xJapanにバイト列を格納する
	if err := json.Unmarshal(jsonBytes, xJapan); err != nil {
		fmt.Println(err)
		return
	}
    for _, members := range xJapan.Members {
		fmt.Printf("NAME: %-7s INSTRUMENT: %s\n", members.Name, members.Instrument)
	}
/*
	出力結果
    NAME: Toshl   INSTRUMENT: vocal
	NAME: PATA    INSTRUMENT: guitar
	NAME: HEATH   INSTRUMENT: bass
	NAME: SUGIZO  INSTRUMENT: guitar
	NAME: YOSHIKI INSTRUMENT: drums
*/

	for _, song := range xJapan.Songs {
		fmt.Println(song)
	}
/*
    出力結果
    紅
    Silent Jealousy
*/
}
```

## func Marshal(v interface{}) ([]byte, error)
json.Marshal関数は、構造体のフィールド名をJSONのキー名として、JSON文字列を生成します。

```go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
)

type Musician struct {
	Name string `json:"name"`
	Instrument string `json:"instrument"`
}

type XJapan struct {
	Members []Musician `json:"members"`
	Songs  []string `json:"songs"`
}

func main() {
	// XJapan構造体のポインターを取得
	xJapan := new(XJapan)
	
    // JSON文字列をバイト列にキャスト
	jsonBytes := []byte(xjapanJson)

    // xJapanにバイト列を格納する
	if err := json.Unmarshal(jsonBytes, xJapan); err != nil {
		fmt.Println(err)
		return
	}

	// 構造体をJSON文字列に変換
	xJapanJson, err := json.Marshal(xJapan)
	if err != nil {
		fmt.Println(err)
		return
	}
	out := new(bytes.Buffer)
	// スペース4つを追加しJSONを整形
	json.Indent(out, xJapanJson, "", "    ")
	fmt.Println(out.String())
/*
	出力結果
	{
		"members": [
			{
				"name": "Toshl",
				"instrument": "vocal"
			},
			{
				"name": "PATA",
				"instrument": "guitar"
			},
			{
				"name": "HEATH",
				"instrument": "bass"
			},
			{
				"name": "SUGIZO",
				"instrument": "guitar"
			},
			{
				"name": "YOSHIKI",
				"instrument": "drums"
			}
		],
		"songs": [
			"紅",
			"Silent Jealousy"
		]
	}
*/
}
```

とても簡単ですね。

# タグ
しれっと出てきましたが、構造体のフィールドについている、`json:"〇〇〇"`は何かと思いますよね。

go言語の構造体には、タグと呼ばれるメタデータを付与できます。このタグ情報は、reflectを用いることで実行時に参照できます。
encoding/jsonパッケージでも内部でreflectを使い、構造体の変数とJSONのキーを紐づけしています。

```go
// タグをつける
Name string `json:"name"`

// JSONタグを付与しなくても、JSONとのマッピングは行われる
Name string

// フィールドが小文字でもOK
name string

// JSONタグを付与すれば、JSONのキー名と全く違う名前のフィールドを定義できる
ABC  string `json:"name"`
``` 
[ドキュメント](https://golang.org/pkg/encoding/json/#Unmarshal)を確認すると、大文字小文字は区別しないと書いてありました。

> To unmarshal JSON into a struct,Unmarshal matches incoming object keys to the keys used by Marshal (either the struct field name or its tag),preferring an exact match but also accepting a case-insensitive match. 
> Unmarshal will only set exported fields of the struct.

ただし、最後の一文によると、別パッケージで定義された構造体に値をセットしたい場合、エクスポートされたフィールドにのみセットされるので注意が必要です。
go言語の仕様上、先頭の文字が大文字である変数・関数のみがパッケージ外でも使用できます。

タグには、以下のようにオプションを付与できます。

```go
// `json:"-"`
// ハイフンを指定すると、このフィールドは無視される
Name string `json:"-"`

// `json:",omitempty"`
// "omitempty"を指定すると、JSONに"name"キーの値が空の場合、このフィールドを削除する
// 空の状態とは、false, 0, nilポインター、nilインターフェース、要素のないarray・slice・map・stringとのこと
Name string `json:"name,omitempty"`

// `json:",string"`
// 強制的に文字列で出力する
Int64String int64 `json:",string"`
```
# 階層構造を持つJSONの扱い
先ほどまでは、以下のように、個別に構造体を定義していました。

```go
// 階層ごとに構造体を定義
type Musician struct {
	Name string `json:"name"`
	Instrument string `json:"instrument"`
}

type XJapan struct {
	Members []Musician `json:"members"`
	Songs  []string `json:"songs"`
}
```

以下のように、JSONの階層構造を、構造体の入れ子構造で表すことができます。

```go
// 構造体を入れ子にできる
// 個別の構造体を定義した場合と同じ意味！
type XJapan struct {
	Members []struct {
		Name       string `json:"name"`
		Instrument string `json:"instrument"`
	} `json:"members"`
	Songs []string `json:"songs"`
}
```
シンプルですね。
より複雑なJSONを扱う場合は、以下の投稿が参考になりました。
[Go言語でJSONに泣かないためのコーディングパターン](https://qiita.com/minagawa-sho/items/dc524e38073ed8e3831b)

# おわりに
go言語でのJSONの操作方法を見てきました。
標準パッケージでかなりのことまで簡単にできることがわかりました。

まだまだ知らないテクニックがありそうですが、今回はここまでです。
周囲の人をgo言語色に洗脳するために、少しずつでも勉強していきたいです。