ヴァル研究所アドベントカレンダー2018の21日目です。
弊社アドベントカレンダーでは今年も「駅すぱあとWebサービス」についていくつかの記事でご紹介してきました。「駅すぱあとWebサービス」は経路探索サービス「駅すぱあと」の持つ機能や情報をWebAPIで利用できるサービスです。
便利な機能が沢山あって最高です!と言いたいところですが、いくつかの辛いポイントもあります。
今回はハマりポイントのうち、駅すぱあとWebサービスのレスポンス構造について解説することで、みなさんがハマらずに「駅すぱあとWebサービス」を使ってもらえればと思います!!
普通に「駅すぱあとWebサービス」を知りたい方はこちら
- アクセスキー要らず!!API Testerで 駅すぱあとWebサービスに簡単入門する
- 「駅すぱあとWebサービス」個人的推し機能3選(範囲探索・住所情報からの周辺駅検索・バス路線時刻表)
- 駅すぱあとWebサービスの範囲探索APIと駅すぱあと路線図を組み合わせてアプリを作ってみる
ちょっと癖のあるレスポンス構造
前提
今回、紹介する問題はJSONでレスポンスを取得した際におきるので、以下の例でもformat
パラメータに「json」を指定することで、JSONでの結果取得を行なっています。
(XMLでの取得では以下の問題は発生しません!)
取得結果を表す要素が複数の場合
ここでは/v1/{format}/station
で呼ぶことのできる駅情報機能を例とします。駅情報機能ではname
パラメータに文字列を指定することで、その文字列で駅名検索をかけることができます。またtype
パラメータを指定することで、取得する駅の交通種別を絞ることも可能です。
例えば以下のようにリクエストしてみましょう
https://api.ekispert.jp/v1/json/station?&name=東京&type=train&key={your_accessskey}
{
ResultSet: {
(略)
max: "5",
offset: "1"
Point: [
{
Station: {
code: "22828",
Name: "東京",
Type: "train",
Yomi: "とうきょう"
},
(略)
},
{
Station: {
Name: "とうきょうスカイツリー",
(略)
},
(略)
},
{
Station: {
Name: "東京テレポート",
(略)
(以下略)
こんな感じで、「東京」で始まる鉄道駅を取得することができます。$.ResultSet.max
の要素を見ると「東京」で始まる鉄道駅は5駅あることもわかります。
$.ResultSet.Point
が指すハッシュのvalueが配列になっており、その中に各駅を表す要素がハッシュとして含まれています。ですので例えば東京駅の駅コードを取得したい場合は$.ResultSet.Point[0].Station.code
を指定します。
取得結果を表す要素が単数の場合
次に以下のようにリクエストしてみます。
https://api.ekispert.jp/v1/json/station?&name=東京テレポート&type=train&key={your_accessskey}
{
ResultSet: {
(略)
max: "1",
offset: "1",
Point: {
Station: {
code: "29073",
Name: "東京テレポート",
Type: "train",
Yomi: "とうきょうてれぽーと"
},
(略)
次は「東京テレポート」で始まる鉄道駅を検索しました。$.ResultSet.max
を見ると「東京テレポート」で始まる鉄道駅は1駅のみであることがわかります。
では東京テレポート駅の駅コードを取得したい時も先ほどと同様に$.ResultSet.Point[0].Station.code
で取得できるでしょうか?
この時、参照エラーが返るはずです。
先ほどの「東京」でリクエストした際のレスポンスと見比べると$.ResultSet.Point
が指すハッシュのvalueには、駅を表す要素であるハッシュが、配列の中ではなくそのまま入っていることがわかります。
よって東京テレポート駅の駅コードを取得したい時は$.ResultSet.Point.Station.code
を指定する必要があります。
対処法
現象を紹介しましたが、このままでは辛いだけなので、サクッと活用できるワンライナーをご紹介。
どの例でもpoints
に先ほどのレスポンス例の$.ResultSet.Point
が代入されていると考えてください。
pythonなら
points = points if type(points) is list else [points]
print(points)
rubyなら
points = [points] if !points.instance_of?(Array)
p points
JavaScriptなら (社内で @t_ryusuke さんに教えていただきました)
var points = (ResultSet.Point instanceof Array) ? ResultSet.Point : [ResultSet.Point];
console.log(points);
これならレスポンスの$.ResultSet.Point
に1駅入っている時も、複数駅入っている時もpoints
には配列が入ります。
なので東京テレポート駅の駅コードはpoints[0].Station.code
で取ることができますね!!
まとめ
ここでは駅情報機能を例にとり、結果が複数だった場合と単数だった場合のレスポンス構造の違いについて説明しました。
同様のことは、経路探索機能(/search/course/extreme
)のCourse
要素や、範囲探索機能(/search/multipleRange
)のPoint
要素などでも発生します。
駅すぱあとWebサービスはリリース当時、XMLでレスポンスを返すことを想定していました。そのあとJSONの需要が高まり、JSONでもレスポンスが取得できる機能を追加したという経緯があります。そのため内部でXML→JSONの変換をかける際にこの問題が発生してしまっています。使いづらくてごめんなさい
なおXML版ではこの問題は発生しません。
駅すぱあとWebサービスを触り始めた方が「ハマって」しまいやすいポイントをご紹介しました。
皆さんはこの点を回避して楽しい実装ライフを送ってください!!