Androidの高速なJSON パーサ/ジェネレータ、LoganSquareを使う

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処理のパフォーマンスにお悩みの方はぜひ使ってみてください！