1. kazutoyo

    Posted

    kazutoyo
Changes in title
+Androidの高速なJSON パーサ/ジェネレータ、LoganSquareを使う
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,250 @@
+Androidのアプリでそこそこ巨大なJSONをパースする必要があったのですが、jackson-databind や GSONではパースに時間がかかりすぎて辛い。。。
+またJacksonのStreaming APIはパフォーマンスはめちゃくちゃいいのですが、パース処理を自分で書いていくのは大変。。。
+
+そんな時に見つけたのがLoganSquareです。
+https://github.com/bluelinelabs/LoganSquare
+
+LoganSquareはモデルクラスにアノテーションを付け、それによりJacksonのStreaming APIを使用したパース処理が生成されます。
+つまりお手軽にJackson Streaming APIの恩恵を受けることが出来ます!
+
+こちらがベンチマークの結果。速い。
+![ベンチマーク](https://qiita-image-store.s3.amazonaws.com/0/8932/983cf60b-b02c-f778-a907-c885940996d5.png)
+
+
+# LoganSquareの使い方
+
+## セットアップ
+build.gradleにLoganSquareを追加します。
+
+```build.gradle
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
+ }
+}
+apply plugin: 'com.neenbedankt.android-apt'
+
+dependencies {
+ apt 'com.bluelinelabs:logansquare-compiler:1.1.0'
+ compile 'com.bluelinelabs:logansquare:1.1.0'
+}
+```
+
+## Proguard
+Proguardの設定はこのように
+
+```proguard-rules.pro
+-keep class com.bluelinelabs.logansquare.** { *; }
+-keep @com.bluelinelabs.logansquare.annotation.JsonObject class *
+-keep class **$$JsonObjectMapper { *; }
+```
+
+## Modelの作成
+
+LoganSquareではモデルクラスに `@JsonObject` アノテーションをつけることにより、そのクラスのフィールドとJSONをマッピングします。
+
+LoganSquareのパースさせるモデルクラスを作成するには次の通り3つのやり方があります。
+
+### 1. すべてのフィールドにアノテーションを付けるやり方
+
+デフォルトな方法はこちら。
+パースするフィールドに `JsonField` アノテーションを付けます。
+
+
+```java
+
+@JsonObject
+public class User {
+
+ @JsonField
+ public long id;
+
+ @JsonField
+ String name;
+
+ @JsonField
+ private String email;
+
+ private String company;
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getCompany() {
+ return company;
+ }
+
+ public void setCompany(String company) {
+ this.company = company;
+ }
+}
+```
+
+この例ではpublicな `id` とパッケージローカルフィールドの `name` 、Getter/Setterのある `email` がパースされます。
+`@JSONField` がついていない `companay` はパースされません。
+
+この方法だとパースする全てのフィールドに `@JSONField` アノテーションを付けなければいけません。
+フィールド数が多い場合は、次の2や3の方法が良いかと思います。
+
+### 2. private以外のフィールドをパースするやり方
+
+private以外の定義されているフィールドをパースする方法です。
+publicまたはパッケージローカルの全てのフィールドがパースされます。
+
+`@JsonObject` に `fieldDetectionPolicy = JsonObject.FieldDetectionPolicy.NONPRIVATE_FIELDS` と指定してやります。
+
+```java
+
+@JsonObject(fieldDetectionPolicy = JsonObject.FieldDetectionPolicy.NONPRIVATE_FIELDS)
+public class User {
+
+ public long id;
+
+ public String name;
+
+ String email;
+
+ @JsonIgnore
+ public String company;
+
+}
+```
+
+`@JsonIgnore` でprivate以外のフィールドをパースから除外することも出来ます。
+
+### 3. private以外、またはアクセサーがあるフィールドをパースするやり方
+
+2のやり方 + アクセサーを持つフィールドがパースされます。
+`@JsonObject` に
+`fieldDetectionPolicy = JsonObject.FieldDetectionPolicy.NONPRIVATE_FIELDS_AND_ACCESSORS` を指定します。
+
+```java
+
+@JsonObject(fieldDetectionPolicy = JsonObject.FieldDetectionPolicy.NONPRIVATE_FIELDS_AND_ACCESSORS)
+public class User {
+
+ public long id;
+
+ public String name;
+
+ private String email;
+
+ @JsonIgnore
+ public String company;
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+}
+```
+
+
+## JSONのパース
+
+InputStreamを渡す方法とStringを渡す方法があります。
+
+### InputStreamからパース
+
+```java
+
+InputStream is = ...;
+User user = LoganSquare.parse(is, User.class);
+```
+
+### Stringからパース
+```java
+
+String jsonStr = "{\"id\": 12345,\"name\": \"kazutoyo\",\"email\": \"kazutoyo@email.address\",\"age\": 25,\"company\": \"(๑´ڡ`๑)ぺろり\"}";
+User User = LoganSquare.parse(jsonStr, User.class);
+```
+
+## JSONの生成
+
+OutputStreamに流す方法とStringにする方法があります。
+
+### OutputStreamへ
+```java
+
+LoganSquare.serialize(user, os);
+```
+
+### Stringへ
+
+```java
+
+String jsonStr = LoganSquare.serialize(user);
+```
+
+# その他
+
+## フィールド名の指定
+モデルのフィールド名とJSONのフィールド名が違う場合、 `@JsonField` に `name` オプションを指定することができます。
+
+```json
+{"_id": 12345}
+```
+
+といったJSONのフィールド名 `_id` で、モデルでのフィールド名が `id` と定義していた場合
+
+```java
+
+@JsonField(name = "_id")
+public long id;
+```
+
+とすることでマッピングできます。
+
+## TypeConverter
+LoganSquareにはTypeConverterというパースする際にデータを整形してくれる機能があります。
+例えば、intの値を日本円の表示に変換するTypeConverterは次のような感じです。
+
+```CurrencyFormatConverter.java
+public class CurrencyFormatConverter extends IntBasedTypeConverter<String> {
+
+ @Override
+ public String getFromInt(int i) {
+ return NumberFormat.getCurrencyInstance(Locale.JAPAN).format(Integer.valueOf(i));
+ }
+
+ @Override
+ public int convertToInt(String object) {
+ try {
+ NumberFormat.getCurrencyInstance(Locale.JAPAN).parse(object).intValue();
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ return 0;
+ }
+}
+```
+
+```Item.java
+@JsonObject(fieldDetectionPolicy = JsonObject.FieldDetectionPolicy.NONPRIVATE_FIELDS_AND_ACCESSORS)
+public class Item {
+
+ int id;
+
+ String name;
+
+ @JsonField(typeConverter = CurrencyFormatConverter.class)
+ String price;
+}
+
+```
+
+# まとめ
+
+このように、LoganSquareは手軽にJackson Streaming APIを扱うことが出来ます。
+JSON処理のパフォーマンスにお悩みの方はぜひ使ってみてください!