Goでネスト構造を持つ&配列型のJSONファイルを読み込む方法
読み込むJSONデータ
Twitter APIからのレスポンスを模したJSONをGoで読み込んでみます。
(実際のTwitter APIからのレスポンスにはもっと多くのデータが含まれていますが、ここでは簡略化しています)
timeline.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
[
{
"created_at": "Wed Dec 16 13:55:51 +0000 2020",
"id": 1145148108931919334,
"full_text": "\u798F\u5CA1\u770C\u6C11\u304B\u3089\u306E\u304A\u77E5\u3089\u305B\u3067\u3059\u3002\u798F\u5CA1\u770C\u3067\u306F\u73FE\u5728\u3001\u6DF1\u591C\u306E\u98EF\u30C6\u30ED\u64B2\u6EC5\u904B\u52D5\u3092\u5B9F\u65BD\u3057\u3066\u3044\u307E\u3059\u3002\u6DF1\u591C\u306E\u98EF\u30C6\u30ED\u306F\u3057\u306A\u3044\u3001\u3055\u305B\u306A\u3044\u3001\u8A31\u3055\u306A\u3044\u3002\u798F\u5CA1\u770C\u304B\u3089\u3001\u6DF1\u591C\u306E\u98EF\u30C6\u30ED\u3092\u306A\u304F\u3057\u307E\u3057\u3087\u3046",
"user": {
"id": 123456789,
"name": "\u671d\u9727",
"screen_name": "asagiri96mc"
}
},
{
"created_at": "Wed Dec 16 13:48:39 +0000 2020",
"id": 1234567890987654321,
"full_text": "\u6765\u5E74\u306EC99\u7533\u3057\u8FBC\u307F\u307E\u305B\u3093\u3067\u3057\u305F\uFF01\u300E\u30A6\u30FC\u30D1\u30FC\u30EB\u30FC\u30D1\u30FC\u3067\u3082\u308F\u304B\u308B\u0047\u006F\u8A00\u8A9E\u300F\u3092\u9812\u5E03\u3044\u305F\u3057\u307E\u305B\u3093\u3002\u5834\u6240\u306F\u81EA\u5B85\u3067\u3059\u3002\u3088\u308D\u3057\u304F\u304A\u9858\u3044\u3057\u307E\u3059\u3002",
"extended_entities": {
"media": [
{
"id": 1234567898765432101,
"media_url_https": "https://pbs.twimg.com/media/EpC3IbJVQAEDpMm.jpg"
}
]
},
"user": {
"id": 987654321,
"name": "orangeliner.net",
"screen_name": "orangeliner_"
}
},
{
"created_at": "Wed Dec 16 11:49:00 +0000 2020",
"id": 1234567890123456789,
"full_text": "\u306D\u3053\u3082\u3075\u3082\u3075\uFF5E",
"extended_entities": {
"media": [
{
"id": 1122334455667788990,
"media_url_https": "https://pbs.twimg.com/media/EpSQAe2U8AYLBsA.jpg"
},
{
"id": 1122334455667788999,
"media_url_https": "https://pbs.twimg.com/media/EpNKZOXVoAAMczK.jpg"
}
]
},
"user": {
"id": 123456789,
"name": "\u671d\u9727",
"screen_name": "asagiri96mc"
}
}
]
Goで読み込む
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
)
// [1] 構造体のフィールドとJSONのキーを紐付け
type Tweet struct {
Date string `json:"created_at"`
ID int `json:"id"`
Text string `json:"full_text"`
Entities Entity `json:"extended_entities"`
User User `json:"user"`
}
// [2] JSONと同様のネスト構造で構造体を定義
type Entity struct {
Media []*Media `json:"media"`
}
type Media struct {
ID int `json:"id"`
URL string `json:"media_url_https"`
}
type User struct {
// 使わないJSONキーは取り込まなくても問題ない
// ID int `json:"id"`
Name string `json:"name"`
ScreenName string `json:"screen_name"`
}
func main() {
file, err := ioutil.ReadFile("timeline.json")
if err != nil {
// エラー処理
}
// [3] 配列型のJSONデータを読み込む
timeline := make([]*Tweet, 0)
err = json.Unmarshal(file, &timeline)
if err != nil {
// エラー処理
}
// スライスの要素数を取得
fmt.Printf("全 %d 件\n\n", len(timeline))
// スライスの要素を順に取り出す
for _, tweet := range timeline {
// [2] ネストした構造体のフィールドを呼び出す
fmt.Printf("%s (@%s)\n", tweet.User.Name, tweet.User.ScreenName)
fmt.Println(tweet.Text)
// .Entities.Media が存在しない要素もあるので、0個でないことを確認
if len(tweet.Entities.Media) != 0 {
for i, media := range tweet.Entities.Media {
fmt.Printf("画像URL[%d]: %s\n", i, media.URL)
}
}
fmt.Printf("ID: %d Posted at: %s\n\n", tweet.ID, tweet.Date)
}
}
実行結果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ go run load_json.go
全 3 件
朝霧 (@asagiri96mc)
福岡県民からのお知らせです。福岡県では現在、深夜の飯テロ撲滅運動を実施しています。深夜の飯テロはしない、させない、許さない。福岡県から、深夜の飯テロをなくしましょう
ID: 1145148108931919334 Posted at: Wed Dec 16 13:55:51 +0000 2020
orangeliner.net (@orangeliner_)
来年のC99申し込みませんでした!『ウーパールーパーでもわかるGo言語』を頒布いたしません。場所は自宅です。よろしくお願いします。
画像URL[0]: https://pbs.twimg.com/media/EpC3IbJVQAEDpMm.jpg
ID: 1234567890987654321 Posted at: Wed Dec 16 13:48:39 +0000 2020
朝霧 (@asagiri96mc)
ねこもふもふ~
画像URL[0]: https://pbs.twimg.com/media/EpSQAe2U8AYLBsA.jpg
画像URL[1]: https://pbs.twimg.com/media/EpNKZOXVoAAMczK.jpg
ID: 1234567890123456789 Posted at: Wed Dec 16 11:49:00 +0000 2020
[1] 構造体のフィールドとJSONのキーを紐付ける
- JSON
1 2 3 4 5 6
{ "created_at": "Wed Dec 16 13:48:39 +0000 2020", "id": 1234567890987654321, "full_text": "...", ... }
- Go
1 2 3 4 5 6
type Tweet struct { Date string `json:"created_at"` ID int `json:"id"` Text string `json:"full_text"` ... }
構造体の定義で `json:"key_name"`
のように記述して、JSONのキーを紐付ける。
json.Unmarshal()
を実行時に、JSONの "key_name"
キーの値が構造体のフィールド(変数)に格納される。
[2] ネストしたJSONを読み込む構造体の定義
- JSON
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
{ ... "extended_entities": { "media": [ { "id": 1234567898765432101, "media_url_https": "https://pbs.twimg.com/media/EpC3IbJVQAEDpMm.jpg" }, ... ] }, "user": { "id": 987654321, "name": "orangeliner.net", "screen_name": "orangeliner_" }, ... }
- Go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
type Tweet struct { ... Entities Entity `json:"extended_entities"` User User `json:"user"` ... } type Entity struct { Media []*Media `json:"media"` } type Media struct { ID int `json:"id"` URL string `json:"media_url_https"` } type User struct { Name string `json:"name"` ScreenName string `json:"screen_name"` }
JSONのネスト構造に合わせて、構造体もネスト構造で定義する。
この例では、「Tweet
型に含まれるフィールド Entities は Entity
型、Entity
型に含まれるフィールド Media は Media
型を含むスライス、Media
型に含まれるフィールド ID は…」といった形で入れ子構造になっている。
- Go
1 2 3 4 5 6
// スライス timeline にデータが格納されているとする timeline[0].Entities.Media[0].ID // => 1234567898765432101 timeline[0].User.ScreenName // => "orangeliner_"
ネストした構造体のフィールドは、フィールド名を .
(ドット)で繋げることで取得できる。
スライスが含まれる場合は [i]
でインデックスを指定する。
[3] 配列型のJSONデータを読み込む
- Go
1 2
timeline := make([]*Tweet, 0) err = json.Unmarshal(file, &timeline)
データを格納する型として、構造体ではなく、要素数 0 のスライスを作成する。
JSONデータ内の配列の要素をこのスライスに格納する。