Go
golang

Golangでinterface{}を使ったJSONのパース

More than 1 year has passed since last update.

前提

例としてslackのusers.listのresponseを見てみます。

{
    "ok": true,
    "members": [
        {
            "id": "U023BECGF",
            "team_id": "T021F9ZE2",
            "name": "bobby",
            "deleted": false,
            "status": null,
            "color": "9f69e7",
            "real_name": "Bobby Tables",
            "tz": "America\/Los_Angeles",
            "tz_label": "Pacific Daylight Time",
            "tz_offset": -25200,
            "profile": {
                "avatar_hash": "ge3b51ca72de",
                "first_name": "Bobby",
                "last_name": "Tables",
                "real_name": "Bobby Tables",
                "email": "bobby@slack.com",
                "skype": "my-skype-name",
                "phone": "+1 (123) 456 7890",
                "image_24": "https://...",
                "image_32": "https://...",
                "image_48": "https://...",
                "image_72": "https://...",
                "image_192": "https://..."
            },
            "is_admin": true,
            "is_owner": true,
            "has_2fa": false,
            "has_files": true
        },
        ...
    ]
}

理想

こうゆう風にアクセスしたいですよね

// 0番目のユーザー名
json["members"][0]["name"]

// 0番目のユーザーのメールアドレス
json["members"][0]["profile"]["email"]

現実

Golangではjson.Unmarshal()を使ってJSONを読み込むことが出来ますが、上記の用にslice的に読み込んではくれません。
ただ、json.Unmarshal()は引数に*interface{}を渡せるので、そこから値をアクセス出来ます。

やってみる

var slackJSON = []byte(`/*上のJSON*/`)

// interfaceを渡して読み込ませる
var usersList interface{}
err := json.Unmarshal(slackJSON, &usersList)
//なんらかのエラー処理

// 0番目のユーザー名
usersList.(map[string]interface{})["members"].([]interface{})[0].(map[string]interface{})["name"].(string)

// 0番目のユーザーのメールアドレス
usersList.(map[string]interface{})["members"].([]interface{})[0].(map[string]interface{})["profile"].(map[string]interface{})["email"].(string)

このようにアクセスすることが出来ます。

何をやっているのか

0番目のユーザー名を取得する例で説明すると、

// map[string]interface{} にアサーション
a := usersList.(map[string]interface{})

// "members"をkeyとする要素を取り出す
b := a["members"]

// []interface{} にアサーション
c := b.([]interface{})

// 0番目の要素を取り出す
d := c[0]

// map[string]interface{} にアサーション
e := d.(map[string]interface{})

// "name"をkeyとする要素を取り出す
f := e["name"]

// 最後にfは interface{} なので string にする
// 数値の場合は int にアサーションする
name := f.(string)

こんなことをやってます。

まとめ

// json[0]
json.([]interface{})[0]

// json["test"]
json.(map[string]interface{})["test"]

ベンチマークは調べてない…。