JSONの処理
JSONを扱う場合encoding/jsonを使う。Goの場合、JSONデータを定義した構造体に入れるのが一般的。
JSONの解析
{
"id": 1,
"name": "nana",
"birthday": "08-16",
"vivid_info": {
"color": "red",
"weapon": "Rang"
}
}
このようなデータ構造の場合、以下のように構造体を定義する。
type Person struct {
ID int `json:"id"`
Name string `json:"name"`
Birthday string `json:"birthday"`
VividInfo struct {
Color string `json:"color"`
Weapon string `json:"weapon"`
} `json:"vivid_info"`
}
JSONから構造体のコードを生成してくれるJSON-to-Goを使うと便利。
JSONをデコードするにはjson.Unmarshal
関数を使う。
func Unmarshal(data []byte, v interface{}) error
デコードされたデータは以下のようにして扱うことができる。
var persons []Person
for _, p := range persons{
fmt.Printf("%d : %s : %s : %s : %s\n", p.ID, p.Name, p.Birthday, p.VividInfo.Color, p.VividInfo.Weapon)
}
// 表示結果
// 1 : nana : 08-16 : red : Rang
未知のデータ構造をもつJSONの解析
JSONデータの構造を事前に知らない場合、interfaceを用いて解析する。JSONパッケージではmap[string]interface{}
とinterface{}
構造を採用して任意のJSONオブジェクトを保存する。Goの型とJSONの型の対応は以下。
- boolはJSON booleansを表す。
- float64はJSON numberを表す。
- stringはJSON stringを表す。
- nilはJSON nilを表す。
以下のようなJSONデータがあるとし、この構造を把握していないと仮定する。
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
これをinterface{}の中に解析。
var f interface{}
err := json.Unmarshal(b,&f)
このときfの中にはmap型が保存されている。これらのkeyはstringで、値は空のinterface{}の中に保存される。
f = map[string]interface{}{
"Name": "Wednesday",
"Age": 6,
"Parents": []interface{}{
"Gomez",
"Morticia",
},
}
これらのデータにアクセスするには以下のようにする。
m := f.(map[string]interface{})
for k, v := range m {
switch vv := v.(type) {
case string:
fmt.Println(k, "is string", vv)
case int:
fmt.Println(k, "is int", vv)
case float64:
fmt.Println(k, "is float64", vv)
case []interface{}:
fmt.Println(k, "is an array:")
for i, u := range vv {
fmt.Println(i, u)
}
default:
fmt.Println(k, "is of a type I dont know how to handle")
}
}
(めんどくさいのでJSONを取得してJSON-to-Goを利用して構造体にぶち込んだほうが見通しが良さそう。)
go-simplejson
上の例よりも簡単にJSONを扱うことのできるパッケージ
go get github.com/bitly/go-simplejson
使い方
下記のjsonを扱う場合、
[simple.json]
{
"hoge": true,
"piyo":{
"foo":[1,2],
"bar":"test"
}
}
import(
simplejson "go-simplejson"
)
//jsonファイルの読み込み
bytes, err := ioutil.ReadFile("./simple.json")
if err != nil {
log.Fatal(err)
}
// []byte型からjson型へ変換
json,err := simplejson.NewJson(bytes)
// Get("hoge").Type()またはGet("hoge").MustType()でアクセスできる。
// Type()は値とエラーを返す
b, _ := json.Get("hoge").Bool() // => true
// MustType()は値のみ
m := json.Get("piyo").MustMap() // => map[bar:test foo:[1 2]]
a, _ := json.Get("piyo").Get("foo").Array() // => [1,2]
// GetPath()で一括記述
s := js.GetPath("piyo", "bar").MustString() // => test
以下のような、配列で構成されるJSONデータの扱いは
[
{
"id": 1,
"name": "akane",
"birthday": "08-16",
"vivid_info": {
"color": "red",
"weapon": "Rang"
}
},
{
"id": 2,
"name": "aoi",
"birthday": "06-17",
"vivid_info": {
"color": "blue",
"weapon": "Impact"
}
},
{
"id": 3,
"name": "wakaba",
"birthday": "05-22",
"vivid_info": {
"color": "green",
"weapon": "Blade"
}
},
{
"id": 4,
"name": "himawari",
"birthday": "07-23",
"vivid_info": {
"color": "yellow",
"weapon": "Collider"
}
},
{
"id": 0,
"name": "rei"
}
]
このようにforで回してアクセス。他にもっといい方法あったら教えてください。
for i, _ := range json.MustArray() {
fmt.Printf("id: %d\n", json.GetIndex(i).Get("id").MustInt())
fmt.Printf("name: %s\n", json.GetIndex(i).Get("name").MustString())
fmt.Printf("birthday: %s\n", json.GetIndex(i).Get("birthday").MustString())
fmt.Printf("vivid_info:color: %s\n", json.GetIndex(i).Get("vivi_info").Get("color").MustString())
fmt.Printf("vivid_info:weapon: %s\n", json.GetIndex(i).Get("vivi_info").Get("weapon").MustString())
}
rubyに比べると少し面倒な印象。