三行まとめ
- puree-androidを全面的に
com.google.gson.JsonObject
を使うように書き換えてv3にした - JsonObjectはJSONObjectとほぼ同様に使うことが出来るがチェック例外がないのでほとんどの用途で使いやすい
- JsonObjectの構築はJSONObjectより少し高速で文字列化の速度はほぼ同じ
概要
Pureeを v3 をリリースしました。
Pureeについては モバイルアプリのログ収集ライブラリ「Puree」をリリースしました - クックパッド開発者ブログ にて。製品に導入して半年たちましたが、いくつかバグ修正しつつ大きな問題もなく運用しています。
ところでこのほどPureeを大きく書き換えてv3としました。これは、 org.json.JSONObject
を使うのをやめて、 com.google.gson.JsonObject
を全面的に採用したのがもっとも大きい変更です。また、 Puree
のstatic methodはほぼすべて PureeLogger
のインスタンスメソッドを呼び出すだけになりました。これによりPureeLog関連のテストを書いたり、テスト時にPureeを無視したりするのが非常に簡単になりました。
Gson vs JSONObject
Gson はJavaオブジェクトをそのままJSONにシリアライズしたりデシリアライズしたりする機能をもつJSONライブラリです。Gsonの提供するクラスに org.json.JSONObject
とほぼ同じ機能をもつ com.google.gson.JsonObject
というクラスがあり、これを使うことでJSONObjectを使わずに済むというわけです。
ご存知の通り、JSONObjectは過剰にチェック例外(JSONException)を使っていて、確実に例外を投げない場面でもtry-catchしなければいけません。下手に JSONObject#put(String key, Object value)
というメソッドがあるために型チェックが働かないというのもインターフェイスの設計ミスといえます。現実的には、プログラム中で静的にJSONObjectを構築する限りJSONExceptionが投げられることはほんとんどないにも関わらずです。そして、ほとんど投げられることのないチェック例外のあるコードを多用していると、アプリケーション自体のコードが徐々に荒れてきます。
GsonのJsonObjectの構築するメソッド群には、Objectを入れるメソッドがありません。そして、チェック例外もありません。煩雑なtry-catchはなく、扱いは簡単で、コードの秩序も保たれます。 JSONをMap<K, V>
のように構築したいのであれば、GsonのJsonObjectを使うほうがいいでしょう。
ただし、GsonはJSONObjectよりも寛容すぎる点もあります。たとえば、NaNとInfinityはJSON標準では認められない値ですが、Gsonはシリアライズしてしまいます。つまり、以下のようになります。
public class GsonTest { @Test public void testJsonObjectSerialization() throws Exception { JsonObject json = new JsonObject(); json.addProperty("nan", Double.NaN); json.addProperty("pinf", Double.POSITIVE_INFINITY); json.addProperty("ninf", Double.POSITIVE_INFINITY); assertThat(json.toString(), is("{\"nan\":NaN,\"pinf\":Infinity,\"ninf\":Infinity}")); } }
一方、JSONObjectはJSON標準通りこれを認めず、例外を投げます。
public class JSONObjectTest { @Test(expected = JSONException.class) public void testJSONObjectSerializationOfNaN() throws Exception { JSONObject json = new JSONObject(); json.put("nan", Double.NaN); } @Test(expected = JSONException.class) public void testJSONObjectSerializationOfPositiveInfinity() throws Exception { JSONObject json = new JSONObject(); json.put("pinf", Double.POSITIVE_INFINITY); } @Test(expected = JSONException.class) public void testJSONObjectSerializationOfNegativeInfinity() throws Exception { JSONObject json = new JSONObject(); json.put("ninf", Double.NEGATIVE_INFINITY); } }
この点は注意が必要です。
パフォーマンス
ざっと測った感じだと、シリアライズはGsonのほうがやや高速で、デシリアライズはほぼ同じ水準でした。高速にならないのは残念ですが、GsonのJsonObjectに切り替えてもパフォーマンスが劣化するということはなさそうです。最近はもっと高速なJSONプロセッサもあるのですが、JSONObjectの代替ということであればGsonでよいのではないかと思っています。