ここが辛いよ:cry: 駅すぱあとWebサービス レスポンス構造編


ヴァル研究所アドベントカレンダー2018の21日目です。

弊社アドベントカレンダーでは今年も「駅すぱあとWebサービス」についていくつかの記事でご紹介してきました。「駅すぱあとWebサービス」は経路探索サービス「駅すぱあと」の持つ機能や情報をWebAPIで利用できるサービスです。

便利な機能が沢山あって最高です!と言いたいところですが、いくつかの辛いポイントもあります。
今回はハマりポイントのうち、駅すぱあとWebサービスのレスポンス構造について解説することで、みなさんがハマらずに「駅すぱあとWebサービス」を使ってもらえればと思います!!

普通に「駅すぱあとWebサービス」を知りたい方はこちら

ちょっと癖のあるレスポンス構造

前提

今回、紹介する問題はJSONでレスポンスを取得した際におきるので、以下の例でもformatパラメータに「json」を指定することで、JSONでの結果取得を行なっています。

(XMLでの取得では以下の問題は発生しません!)

取得結果を表す要素が複数の場合

ここでは/v1/{format}/stationで呼ぶことのできる駅情報機能を例とします。駅情報機能ではnameパラメータに文字列を指定することで、その文字列で駅名検索をかけることができます。またtypeパラメータを指定することで、取得する駅の交通種別を絞ることも可能です。
例えば以下のようにリクエストしてみましょう

request
https://api.ekispert.jp/v1/json/station?&name=東京&type=train&key={your_accessskey}
response
{
  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を指定します。

取得結果を表す要素が単数の場合

次に以下のようにリクエストしてみます。

request
https://api.ekispert.jp/v1/json/station?&name=東京テレポート&type=train&key={your_accessskey}
response
{
  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.py
points = points if type(points) is list else [points]
print(points)

rubyなら

points.rb
points = [points] if !points.instance_of?(Array)
p points

JavaScriptなら (社内で @t_ryusuke さんに教えていただきました)

points.js
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サービスを触り始めた方が「ハマって」しまいやすいポイントをご紹介しました。
皆さんはこの点を回避して楽しい実装ライフを送ってください!!