いろいろな所に書かれているけど,ハマってしまったので書き残しておく.
golangでJSONをパースするにはencoding/jsonを利用すれば良い.
静的言語だけあってちょっと面倒なところがあって,JSONの構造に合わせて前もってstructを定義しておく必要がある.
基本
一番単純な例だと以下のようなJSONを考える.
{ "name":"twinbird", "age":27, "sex":"man" }
これをパースして表示しようとすると以下のようなコードになる.
package main import ( "encoding/json" "fmt" ) const ( json1 = ` { "name":"twinbird", "age":27, "sex":"man" }` ) type Person struct { Name string `json:"name"` Age int `json:"age"` Sex string `json:"sex"` } func main() { bytes := []byte(json1) var p Person err := json.Unmarshal(bytes, &p) if err != nil { fmt.Println("error:", err) return } fmt.Printf("Name: %v, Age: %v, Sex: %v\n", p.Name, p.Age, p.Sex) }
結果は以下になる.
Name: twinbird, Age: 27, Sex: man
ここまでは割と単純だ.
大事なのはstructにタグ(`で囲んだやつ)を付けてJSONのキーに合わせてやること.
タグは単なるメタデータらしいのでライブラリに知らせるためだけに使われることになる.
(大文字,小文字に注意.また,キーはダブルクオーテーションで囲む必要がある.)
ところでタグの付け方がおかしいかを確認するコマンドがある
$ go vet
コンパイル前におかしい箇所を色々指摘してくれる.
配列
次にコレを応用して配列をパースする.
実は次のネストした関係よりこちらの方にハマってしまった.
大したことではないのだが,人間一度視野が狭くなると中々抜け出せない.
package main import ( "encoding/json" "fmt" ) const ( json1 = ` [ { "name":"twinbird", "age":27, "sex":"man" }, { "name":"taro", "age":29, "sex":"man" } ]` ) type Person struct { Name string `json:"name"` Age int `json:"age"` Sex string `json:"sex"` } func main() { bytes := []byte(json1) var people []Person err := json.Unmarshal(bytes, &people) if err != nil { fmt.Println("error:", err) return } for _, person := range people { fmt.Printf("Name: %v, Age: %v, Sex: %v\n", person.Name, person.Age, person.Sex) } }
ほとんど変わっていない.
だが,とても大事なことはjson.Unmarshalに渡す変数がスライスになった事だ.
やってしまえば当たり前なのだが,随分ハマってしまった.
実行結果は以下
Name: twinbird, Age: 27, Sex: man Name: taro, Age: 29, Sex: man
ネスト
ネストしたデータは構造体のネストで表す.
package main import ( "encoding/json" "fmt" ) const ( json1 = ` { "company_name":"FooSystem", "employees":[ { "name":"twinbird", "age":27, "sex":"man" }, { "name":"taro", "age":29, "sex":"man" } ] }` ) type Company struct { Name string `json:"company_name"` Employees []Person `json:"employees"` } type Person struct { Name string `json:"name"` Age int `json:"age"` Sex string `json:"sex"` } func main() { bytes := []byte(json1) var c Company err := json.Unmarshal(bytes, &c) if err != nil { fmt.Println("error:", err) return } fmt.Println(c.Name) for _, person := range c.Employees { fmt.Printf("Name: %v, Age: %v, Sex: %v\n", person.Name, person.Age, person.Sex) } }
これも割と素直.
ちょっと楽する
ところで構造体にタグを書いていましたが実は書かなくて良い状況があります.
JSONのキーが構造体名と完全一致,または大文字小文字の差だけであれば勝手にマッピングしてくれます.
最初の例(タグ無し版)
package main import ( "encoding/json" "fmt" ) const ( json1 = ` { "name":"twinbird", "age":27, "sex":"man" }` ) type Person struct { Name string Age int Sex string } func main() { bytes := []byte(json1) var p Person err := json.Unmarshal(bytes, &p) if err != nil { fmt.Println("error:", err) return } fmt.Printf("Name: %v, Age: %v, Sex: %v\n", p.Name, p.Age, p.Sex) }
結果は以下になる.
Name: twinbird, Age: 27, Sex: man
まとめ
- JSONのパースには構造体にタグ付けをしてUnmarshal
- 配列は構造体を素直にスライスにする
- ネストも構造体を素直にネストする
- 実はJSONのキーと構造体名が同じならタグ付け不要