Ruby 用 JSON パーサーを更新、 JSON への変換も追加
以前公開した前バージョンにはたくさんのアドバイス、リンクなどいただきまして、ありがとうございます。少々時間が経ってしまいましたが、あれからいろいろと勉強しまして、 strscan なる便利なライブラリが Ruby の標準ライブラリに含まれていることも知りました。それらをきちんと使えばコードをだいぶシンプルにできそうだったので、思い切って書き直してみました。まだまだ改善の余地はありますが、以前よりもだいぶ「Ruby っぽい」ソースになっているのではないかと思います。ついでにいくつか機能を追加してみました。従来からある機能も含めて、特徴は以下のとおりです。
- eval などを使わずにまともに解析しているので堅牢である。
- こちらのページのスペックを(今度こそ^^;)満たしている。サロゲート・ペアにも対応しました。
- 他のライブラリに依存していないので、簡単に自分のソースに埋め込める。
- UTF-8 の妥当性チェックを行うので、アプリケーションの脆弱性防止にある程度の効果がある(かもしれない)。
- JSON のパースだけでなく、 Ruby オブジェクトから JSON 文字列することも生成も可能。
近いうちに RubyForge にスニペットとして登録しようと思いますが、一足先にこの場で公開します。もし旧バージョンをお使いの方がおられましたら、ぜひ差し替えてくださいませ。
※ RubyForge に登録しました。最新のコードはこちらをご参照ください。
※ コードなにがしにも登録しました。日本語のコメントが付いているので、こちらのほうがお勧めです。
ソースコード
それでは、まずはソースです。 JsonParser が従来の JSON 文字列を Ruby の配列 or ハッシュに変換するクラス、新たに追加した JsonBuilder がその逆を行うクラスです。通常は単にソースに挿入するだけで使えるようになるはずです。
もし上記のガジェットがうまく動作しない場合は、以下の URL をご参照ください。
JsonParser の使い方
JsonParser クラスは JSON 文字列を Ruby の配列やハッシュに変換するシンプルな JSON パーサーです。基本的には以下のように使います。
parser = JsonParser.new ruby_obj = parser.parse(json_string)
これで、 json_string に格納された JSON 文字列を解析し、配列もしくはハッシュとして返します。 json_string は UTF-8 エンコーディングを前提にしていますので、他の文字コードの場合はあらかじめ UTF-8 に変換しておいてください。また、処理中にエラーが発生すると例外を投げますので、クリティカルな用途に使う場合は例外のハンドリングを忘れないでください。
コンストラクタ
JsonParser クラスのコンストラクタは以下の書式になります。
JsonParser.new(option = {})
option はハッシュで、以下の要素を持つことができます。
| 要素名(Symbol型) | 機能 | デフォルト |
|---|---|---|
| :validation | false なら UTF-8 の検証を行わない | true |
| :surrogate | false ならサロゲート・ペアを処理しない | true |
| :malformed_chr | 不正な UTF-8 シーケンスを置き換える文字 | nil |
validation などによる速度低下が気になる場合、%u 記法を用いて無理やりユニコード以外の文字コードを表現している場合などは検証機能やサロゲート・ペアの変換機能が邪魔になりますので、このオプションで無効にしてください。
parse メソッド
parse メソッドは実際に JSON 文字列を Ruby の配列・ハッシュに変換します。書式は以下のようになります。
def parser(str, option = {})
- str
- 変換元の JSON 文字列です。 UTF-8 エンコーディングを前提にしています。
- option
- コンストラクタの引数と同じハッシュです。コンストラクタで指定したオプションを上書きできます。
戻り値は JSON を変換した配列もしくはハッシュです。
JsonBuilder の使い方
JsonBuilder は Ruby のオブジェクトを JSON 文字列にシリアライズするクラスです。基本的な使い方は以下のようになります。
builder = JsonBuilder.new json_string = builder.build(ruby_obj)
ただし、どのような Ruby オブジェクトでもシリアライズできるわけではなく、以下の条件を満たさなくてはなりません。
- TrueClass, FalseClass, NilClass, Numeric, String, Array, Hash のいずれかのクラスのインスタンス、もしくは to_s メソッドで String に変換できるオブジェクトであること。
- Array, Hash の各要素も上記の条件に従うこと。
- Hash のインデックスは String インスタンス、もしくは to_s メソッドで String に変換できるオブジェクトであること。
build メソッドの引数が Array でも Hash でもない場合、単一要素の配列に変換されます。また、こちらもエラーが発生すると例外を投げるので、ハンドリングを忘れないようにしてください。オプション引数などはとくにないので、詳細は省略します。上記のサンプルがすべてです(^^;
UTF-8 検証機能の詳細
今回のバージョンから、新たに UTF-8 文字列の妥当性チェックを行う機構が実装されています。具体的なチェック内容は以下のとおりです。
- マルチバイトコードが途中で途切れていないか。
- 0x80〜0xbf (マルチバイトの 2byte 以降に使われるコード)がいきなり出現していないか。
- UTF-16 に変換できない領域が使われていないか。
- 不必要に長い不正なコードが使われていないか(ASCII コードをマルチバイトで表現しているなど)。
- サロゲートペア用の領域が使われていないか。
- null 文字が含まれていないか。
これらのチェックに引っかかると、その文字を malformed_chr オプションで指定した文字に置き換えます(オプションが指定されていないか nil の場合は例外を投げます)。これにより、不正な UTF-8 シーケンスによるエスケープ抜けや区切り文字の読み飛ばしなどの危険がだいぶ減るのではないかと思います。ただし、単純にエンコーディングの規則上あり得ないコードを弾くだけなので、必ずしも文字が定義されていることを保証するわけではありません。あらかじめご了承ください。
上記の検証内容はとりあえず私が思いつく範囲で実装したものなので、ご意見をお待ちしています。個人的には例外を投げずに空白などに置き換えるだけに留めたほうが良いかなどが迷いどころです。その他、抜けや不都合がありましたら、ぜひお知らせください。
ライセンスについて
前回は基本自由だけど必要があれば New BSD / Ruby ライセンス / LGPL のいずれかなんていう曖昧な感じでしたが、余計に混乱する気がしてきたので、パブリックドメインということで一本化しようと思います。要するに好きに使ってください。著作者名の表示などもなくてかまいません。その気になれば一日で作れる代物ですから(笑)。日本では本当の意味でのパブリックドメインはあり得ないとかなんとかあるらしいですが、ま、固いことは言わない方向で。
その代わりというわけではありませんが、当然ながら無保証ですので、使用した結果に関してはすべて自己責任でお願いします。できるだけサポートはしようと思いますが、限界はあります。
このあたりご了承いただいた上で、ご自由にお使いください。
ご意見・ご要望・バグ報告などは Google グループへ
今回から、サポート用に Google グループを設けました。感想などは blog へのコメントでかまいませんが、ご意見やご質問、バグ報告などはこちらを使っていただけると嬉しいです。できるだけ情報集約しておきたいためです。 Google アカウントがあればどなたでも書き込めますので、どしどし投稿してください。
また、今後の細かいバージョンアップなどの情報もグループで配信する予定です。 JsonParser, JsonBuilder クラスをご利用になる方は、ぜひグループへの参加、もしくはフィードの購読をお勧めします。
それでは、パワーアップした JsonParser クラスおよび新規追加の JsonBuilder クラス、ぜひご活用ください!
2007/2/27 追記
JsonParser クラスのオプションのインデックスを文字列から Symbol 型に変更しました。後で直そうと思ってすっかり忘れていました。たいへん申し訳ありません。
2007/3/2 追記
空配列がパースできないなど、いくつかのバグがありました(Rubricks の Shouta さん、ご報告ありがとうございます)。ソースコードを修正版に差し替えました。
2007/3/3 追記
旧バージョンの記事で PuniPuni さんからいただいたコメントを参考に、正規表現の文字コード指定(u ではなく n にしました。もともとバイト列のつもりで作っていたので・・・^^ゞ)、 Unicode の上限の修正を行いました。 ^ と \A については、 strscan 移行時にそもそも ^ がなくなっていました。その他、試験的に JsonBuilder に配列・ハッシュのネストレベルの制限を設けました。
2007/3/21 追記
validate_string のバグをいくつか修正しました。
2007/8/7 追記
さらにいくつかのバグを修正しました。さらに日本語コメントを追加してコードなにがしにアップ。これに伴い、記事にソースを掲載するのはやめることにしました。ご了承ください。
2007/8/17 追記
コードなにがしガジェットを利用して、ソースの掲載を復活させました。
この記事にコメントする