Crystal で JSON パースするには ?
Ruby 風の静的言語 Crystal で JSON をパースするには、以下の 2 つの方法があります。
- JSON::Parser を使う方法
- JSON::Mapping を使う方法
JSON::Parser は、どんな型が入っているかが未定な JSON に有効な方法で、変換後の型を確かめながら (もしくは強制的に型を変換し) 値を取得します。特に動的言語で有効なアプローチです。
JSON::Mapping は、事前にどのような型が入っているかを定義し、それに従ってパースする方法です。こちらのほうが高速で、変換後の値も扱いやすいのでオススメです。この記事では、こちらを解説します。
JSON::Mapping を使ってパースする方法
今回変換するのは、イベント管理サイト Connpass の API 出力です。
- API リファレンス (公式)
- 出力例 (Ruby でキーワード検索した場合)
http://connpass.com/api/v1/event/?keyword=Ruby
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 なので、積極的に使いましょう。