crystal

Crystal で JSON を高速パースする

More than 3 years have passed since last update.

Crystal で JSON パースするには ?

Ruby 風の静的言語 Crystal で JSON をパースするには、以下の 2 つの方法があります。

  • JSON::Parser を使う方法
  • JSON::Mapping を使う方法

JSON::Parser は、どんな型が入っているかが未定な JSON に有効な方法で、変換後の型を確かめながら (もしくは強制的に型を変換し) 値を取得します。特に動的言語で有効なアプローチです。

JSON::Mapping は、事前にどのような型が入っているかを定義し、それに従ってパースする方法です。こちらのほうが高速で、変換後の値も扱いやすいのでオススメです。この記事では、こちらを解説します。

JSON::Mapping を使ってパースする方法

今回変換するのは、イベント管理サイト Connpass の API 出力です。

JSON::Mapping を使ってパースするには、はじめに JSON の構造の定義をし、その定義を用いてパースします

JSON 構造の定義

JSON 構造を定義するには、json_mapping マクロを使います。引数は { key: Type } の形式で指定します。値に null が来る可能性がある場合は、{ key: { type: Type, nilable: true } のように指定します。

require "json"

module Response
  class Result
    JSON.mapping({
      results_returned: Int32,
      results_available: Int32,
      results_start: Int32,
      events: Array(Event),
    })
  end

  class Event
    JSON.mapping({
      event_id: Int32,
      title: { type: String, nilable: true },
      catch: { type: String, nilable: true },
      description: { type: String, nilable: true },
      event_url: { type: String, nilable: true },
      hash_tag: { type: String, nilable: true },
      started_at: { type: String, nilable: true },
      ended_at: { type: String, nilable: true },
      limit: { type: Int32, nilable: true }, 
      event_type: { type: String, nilable: true },
      series: { type: Series, nilable: true },
      address: { type: String, nilable: true },
      place: { type: String, nilable: true },
      lat: { type: String, nilable: true },
      lon: { type: String, nilable: true },
      owner_id: { type: Int32, nilable: true }, 
      owner_nickname: { type: String, nilable: true },
      owner_display_name: { type: String, nilable: true },
      accepted: { type: Int32, nilable: true }, 
      waiting: {type: Int32, nilable: true },
      updated_at: { type: String, nilable: true },
    })
  end

  class Series
    JSON.mapping({
      id: Int32,
      title: { type: String, nilable: true },
      url: { type: String, nilable: true },
    })
  end
end

JSON のパース

上記で定義した構造を用いて、JSON を実際にパースするには、以下のようにします。

require "json"
require "http/client"

# HTTP リクエストを投げて JSON を取得する
res = HTTP::Client.get("http://connpass.com/api/v1/event/?keyword=Ruby")
body = res.body

# 定義した構造を元に JSON をパースする
# 変換結果は Response::Result のインスタンスとして取得されます
result = Response::Result.from_json(body)

p result.title

まとめ

JSON::Mapping を使うと、簡単・高速に JSON パースが可能です。標準 API なので、積極的に使いましょう。

参考文献