Edited at

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 なので、積極的に使いましょう。


参考文献