横浜市交通局 バス路線情報の取得方法 golangによるJSONパーサー
https://ckan.odpt.org/dataset/b_busroute-yokohamamunicipal
■動かなくて悩んでいたコード
// xml_parse.go
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
)
type testXML struct {
Date string `json:"dc:date"`
Busroute string `json:"odpt:BusroutePattern"`
}
func main() {
client := &http.Client{}
req, err := http.NewRequest("GET", "https://api.odpt.org/api/v4/odpt:BusroutePattern?odpt:operator=odpt.Operator:YokohamaMunicipal&acl:consumerKey=f4954c3814b207512d8fe4bf10f79f0dc44050f1654f5781dc94c4991a574bf4", nil)
if err != nil {
log.Fatal(err)
}
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
if err != nil {
log.Fatal(err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
//fmt.Println(body)
var data testXML
err = json.Unmarshal(body, &data)
if err != nil {
fmt.Println("Unmarshal")
log.Fatal(err)
}
fmt.Println(data)
}
出力結果
> go run xml_parse.go
Unmarshal
2023/05/01 18:05:31 json: cannot unmarshal array into Go value of type main.testXML
exit status 1
悩むこと1時間、("type testXML"スキーマを色々いじったりしていた)
『そういえば、このjsonのデータは、違う路線データも入っているはずだよな』
→ 『ということは、複数のデータが入っていることになるよな』
→ 『 For文で取らなければ不味くないか?』
で、ググってみたら、どうやらループになるように仕込んでおかないといけないらしいことが分かりました(XMLの先頭の情報が取れるだけだと思っていた)
■動いたコード
// xml_parse.go
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
)
type testXML struct {
Date string `json:"dc:date"`
Busroute string `json:"odpt:BusroutePattern"`
}
func main() {
client := &http.Client{}
req, err := http.NewRequest("GET", "https://api.odpt.org/api/v4/odpt:BusroutePattern?odpt:operator=odpt.Operator:YokohamaMunicipal&acl:consumerKey=f4954c3814b207512d8fe4bf10f79f0dc44050f1654f5781dc94c4991a574bf4", nil)
if err != nil {
log.Fatal(err)
}
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
if err != nil {
log.Fatal(err)
x
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
//fmt.Println(body)
var data []testXML
err = json.Unmarshal(body, &data)
if err != nil {
fmt.Println("Unmarshal")
log.Fatal(err)
}
for _, e := range data {
fmt.Println(e.Date)
}
}
go run xml_parse.go
2023-04-03T00:00:00+09:00
2023-04-03T00:00:00+09:00
2023-04-03T00:00:00+09:00
2023-04-03T00:00:00+09:00
2023-04-03T00:00:00+09:00
2023-04-03T00:00:00+09:00
2023-04-03T00:00:00+09:00
2023-04-03T00:00:00+09:00
2023-04-03T00:00:00+09:00
2023-04-03T00:00:00+09:00
2023-04-03T00:00:00+09:00
2023-04-03T00:00:00+09:00
2023-04-03T00:00:00+09:00"当たり"だったようです。
バス路線情報のJSONをブラウザで読みとってみました。
ここから、酷いハマり方をします(10時間くらい)。
上記のルートの位置情報の表示のコーディングがどうしても分からない。
以下は、上記のJSONを簡略化したものです(route.json)。
[
{
"dc:date":"2023-04-03T00:00:00+09:00",
"dc:title":"007",
"ug:region":
{
"type":"Triangle",
"coordinates":[[139.6249873,35.4648941],[139.6237514,35.4648862],[139.623672,35.4650137]]
},
"owl:sameAs":"test1"
},
{
"dc:date":"2023-04-03T00:00:00+09:00",
"dc:title":"008",
"ug:region":
{
"type":"LineString",
"coordinates":[[139.667765,35.416456],[139.668006,35.416708],[139.668116,35.416788],[139.668276,35.416841]]
},
"owl:sameAs":"test2"
}
]
問題は、ネストの中に入っていて、タグの名前のついていない、
"coordinates":[[139.6249873,35.4648941],[139.6237514,35.4648862],[139.623672,35.4650137]]
の部分です。
10時間くらいは戦ったかなぁ ―― もう疲れ果てて、質問サイトに投稿する為に、(上記の)JSONファイルと、このパーサープログラムを成形していました。
で、そのプログラムをテストランさせたら ―― これが動いてしまったのです。
// route_json.go
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
)
type testXML struct {
Date string `json:"dc:date"`
Title string `json:"dc:title"`
Note string `json:"odpt:note"`
Owl string `json:"owl:sameAS"`
Regions struct {
Type string `json:"type"`
Coordinates []interface{}
//Coordinates []struct {
// Longitude float64 `json:"longitudes>longitude"`
// Latitude float64 `json:"latitudes>latitude "`
//} `json:"coodtinates"`
} `json:"ug:region"`
}
func main() {
file, err := ioutil.ReadFile("route.json")
//file, err := ioutil.ReadFile("odpt_BusroutePattern.json")
if err != nil {
// エラー処理
}
var data []testXML
err = json.Unmarshal(file, &data)
if err != nil {
fmt.Println("Unmarshal")
log.Fatal(err)
}
fmt.Println(string(file))
// ループによる取得
for _, e := range data {
fmt.Println(e) //
mm := e.Regions.Coordinates
fmt.Println(len(mm))
for _, m := range mm {
fmt.Println(m)
}
}
}
実行結果は以下の通りです。
> go run route_json.go
[
{
"dc:date":"2023-04-03T00:00:00+09:00",
"dc:title":"007",
"ug:region":
{
"type":"Triangle",
"coordinates":[[139.6249873,35.4648941],[139.6237514,35.4648862],[139.623672,35.4650137]]
},
"owl:sameAs":"test1"
},
{
"dc:date":"2023-04-03T00:00:00+09:00",
"dc:title":"008",
"ug:region":
{
"type":"LineString",
"coordinates":[[139.667765,35.416456],[139.668006,35.416708],[139.668116,35.416788],[139.668276,35.416841]]
},
"owl:sameAs":"test2"
}
]{2023-04-03T00:00:00+09:00 007 test1 {Triangle [[139.6249873 35.4648941] [139.6237514 35.4648862] [139.623672 35.4650137]]}}
3
[139.6249873 35.4648941]
[139.6237514 35.4648862]
[139.623672 35.4650137]
{2023-04-03T00:00:00+09:00 008 test2 {LineString [[139.667765 35.416456] [139.668006 35.416708] [139.668116 35.416788] [139.668276 35.416841]]}}
4
[139.667765 35.416456]
[139.668006 35.416708]
[139.668116 35.416788]
[139.668276 35.416841]
ずっとエラーが出ていたんですが、突然表示されるようになりました。
Coordinates []interface{}
JSONタグが付いていない問題は、これで解消できるようです。
で、(簡易版ではなく)実体のJSONファイルを使ってみても動きました。
こういうことがあるから、本当にコーディングというのは厄介なんですよね。
パースしたデータを表示してみました。
通路が(多分)順番に表示されることが確認できました。
だいたいこれで完成です。
// route_json.go
// 横浜市交通局 バス路線情報 / Bus route information of Transportation Bureau, City of Yokohama
// https://ckan.odpt.org/dataset/b_busroute-yokohamamunicipal
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
)
type testJSON struct {
Id string `json:"@id"`
Type string `json:"@type"`
Date string `json:"dc:date"`
Context string `json:"@context"`
Title string `json:"dc:title"`
Note string `json:"odpt:note"`
Regions struct {
Type string `json:"type"`
Coordinates []interface{}
} `json:"ug:region"`
Owl string `json:"owl:sameAS"`
Pattern string `json:"odpt:pattern"`
Busroute string `json:"odpt:busroute"`
Operator string `json:"odpt:operator"`
Direction string `json:"odpt:direction"`
BusstopPoleOrder []struct {
Note string `json:"odpt:note"`
Index int `json:"odpt:index"`
BusstopPole string `json:"odpt:busstopPole"`
} `json:"odpt:busstopPoleOrder"`
}
func main() {
// JSONファイルから読み取る場合
//file, err := ioutil.ReadFile("route.json")
file, err := ioutil.ReadFile("odpt_BusroutePattern.json")
if err != nil {
// エラー処理
}
/*
// Webアクセスで取得する場合
client := &http.Client{}
req, err := http.NewRequest("GET", "https://api.odpt.org/api/v4/odpt:BusroutePattern?odpt:operator=odpt.Operator:YokohamaMunicipal&acl:consumerKey=f4954c3814b207512d8fe4bf10f79f0dc44050f1654f5781dc94c4991a574bf4", nil)
if err != nil {
log.Fatal(err)
}
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
if err != nil {
log.Fatal(err)
}
file, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
*/
var data []testJSON
err = json.Unmarshal(file, &data)
if err != nil {
fmt.Println("Unmarshal")
log.Fatal(err)
}
//fmt.Println(string(file))
// ループによる取得
for _, e := range data {
fmt.Println(e) // 全情報表情
coors := e.Regions.Coordinates
fmt.Println("len_mm", len(coors))
for _, coor := range coors {
fmt.Println(coor)
}
stops := e.BusstopPoleOrder
fmt.Println("len_ss", len(stops))
for _, stop := range stops {
fmt.Println(stop.Note)
fmt.Println(stop.Index)
fmt.Println(stop.BusstopPole)
}
}
}