Elixir: 純粋Elixirの最速JSONパーサ/ジェネレータJason
gumi TECH
Posted on October 23, 2018
Jasonは、Elixir開発者のひとりMichał Muskała氏により純粋なElixirで書かれた最速のJSONパーサおよびジェネレータです。他のライブラリ、とくにPoisonより少なくとも2倍高速です。CでNIFとして実装されているjiffyにも劣りません(処理時間は2倍程度に収まります)。本稿はMuskała氏の許諾を得て、JasonライブラリのREADME.md
にもとづいてその内容をご紹介します。
パーサとジェネレータは、ともにRFC 8259とECMA 404規格に完全に準拠しています。また、パーサーは、JSONTestSuiteを用いてテストされています。
インストール
このパッケージをインストールするには、mix.exs
のdeps
に依存関係としてjason
を加えてください。
def deps do
[{:jason, "~> 1.1"}]
end
基本的な使い方
Jasonによるエンコードとデコードのコードは、たとえばつぎのとおりです。詳しくは、Jasonのドキュメントをご参照ください。
iex> Jason.encode!(%{"age" => 44, "name" => "Steve Irwin", "nationality" => "Australian"})
"{\"age\":44,\"name\":\"Steve Irwin\",\"nationality\":\"Australian\"}"
iex> Jason.decode!(~s({"age":44,"name":"Steve Irwin","nationality":"Australian"}))
%{"age" => 44, "name" => "Steve Irwin", "nationality" => "Australian"}
他のライブラリとの使い方
Postgrex
lib
内の.ex
ファイルで、Postgrex.Types.define/3
によりカスタムの型を定めなければなりません。
Postgrex.Types.define(MyApp.PostgresTypes, [], json: Jason)
## Ectoで使うときはデフォルト拡張を渡さなければなりません
Postgrex.Types.define(MyApp.PostgresTypes, [] ++ Ecto.Adapters.Postgres.extensions(), json: Jason)
これで、Postgrex.start_link/1
にモジュールを渡せば使えるようになります。
Ecto
EctoアプリケーションでPoison
の現行の動作を完全に再現するには、config/config.exs
内でJason
をデフォルトエンコーダに定めなければなりません。
config :ecto, json_library: Jason
さらに、PostgreSQLを使う場合は、config/config.exs
またはconfig/<env>.exs
にカスタム型モジュールを定めてください。
config :my_app, MyApp.Repo, types: MyApp.PostgresTypes
Plug (およびPhoenix)
まず、JSONの解析にJason
を用いるようPlug.Parsers
に定めなければなりません。そして、Plug.Parsers
プラグをどこに差し込むか決めます。Phoenixであれば、エンドポイントモジュールlib/app_web/endpoint.ex
に加えるコードは、たとえばつぎのとおりです。
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Jason
さらに、Phoenixではconfig/config.exs
にエンコーダをつぎのように定めてください。
config :phoenix, :format_encoders,
json: Jason
Phoenixチャンネル用のカスタムJSONエンコーダはもう少し手間がかかります。カスタムシリアライザのコードとその使い方についてはjson_transport_serializer.ex
をご参照ください。
Absinthe
Absinthe.Plug
に:json_codec
オプションを定めてください。
# 直に呼び出すとき
plug Absinthe.Plug,
schema: MyApp.Schema,
json_codec: Jason
# Phoenixルータで使うとき
forward "/api",
to: Absinthe.Plug,
init_opts: [schema: MyApp.Schema, json_codec: Jason]
ベンチマーク
メモリの計測も含めたベンチマークはdecode.txt
をご覧ください。HTMLのパフォーマンスレポートはjason/decode.html
とjason/encode.html
で公開されています。
実行
広く使われているElixirとErlangのJSONライブラリは、mix bench.encode
およびmix bench.decode
でベンチマークが行われます。ベンチマークを実行した結果は、それぞれbench/output/encode.html
とbench/output/decode.html
でお確かめください。
Poisonとの違い
JasonはPoisonといくつか異なる機能があります。
- JSON仕様への準拠はより厳密です。
- JSON文字列内のエスケープされていない改行(たとえば"\"\n\"")はデコードエラーになります。
- データ構造へのデコード(
:as
オプション)には対応しません。 -
MapSet
とRange
およびStream
の組み込みエンコーダーはありません。 - 任意の構造体へのエンコードはサポートされません。
-
Jason.Encoder
プロトコルを明示的に実装してください。
-
- 高品質出力(デフォルト
pretty: true
)のカスタマイズオプションが異なります。
サポートされていないコレクション型のエンコーダが必要な場合には、プロジェクトに直接追加することが推奨されます。
defimpl Jason.Encoder, for: [MapSet, Range, Stream] do
def encode(struct, opts) do
Jason.Encode.list(Enum.to_list(struct), opts)
end
end
プロトコルを実装していない構造体をエンコードしなければならないときは、どのフィールドをJSONにするのが実装で明示できます。
@derive {Jason.Encoder, only: [....]}
defstruct # ...
すべてのフィールドをエンコードすることもできます。けれど、新たなフィールドを加えるとき、誤ってプライベートな情報が漏れることのないように注意してください。
@derive Jason.Encoder
defstruct # ...
ライセンス
JasonはApache 2.0のライセンスでリリースされています。詳しくはLICENSEファイルをご参照ください。
テストとベンチマークについては、Poisonライブラリをもとにしています。このライセンスははじめCC0-1.0とされていました。
Posted on October 23, 2018
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.