Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

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

More than 5 years have passed since last update.

Androidのアプリでそこそこ巨大なJSONをパースする必要があったのですが、jackson-databind や GSONではパースに時間がかかりすぎて辛い。。。
またJacksonのStreaming APIはパフォーマンスはめちゃくちゃいいのですが、パース処理を自分で書いていくのは大変。。。

そんな時に見つけたのがLoganSquareです。
https://github.com/bluelinelabs/LoganSquare

LoganSquareはモデルクラスにアノテーションを付け、それによりJacksonのStreaming APIを使用したパース処理が生成されます。
つまりお手軽にJackson Streaming APIの恩恵を受けることが出来ます!

こちらがベンチマークの結果。速い。
ベンチマーク

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 アノテーションを付けます。

@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またはパッケージローカルの全てのフィールドがパースされます。

@JsonObjectfieldDetectionPolicy = JsonObject.FieldDetectionPolicy.NONPRIVATE_FIELDS と指定してやります。

@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 を指定します。

@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からパース

InputStream is = ...;
User user = LoganSquare.parse(is, User.class);

Stringからパース

String jsonStr = "{\"id\": 12345,\"name\": \"kazutoyo\",\"email\": \"kazutoyo@email.address\",\"age\": 25,\"company\": \"(๑´ڡ`๑)ぺろり\"}";
User User = LoganSquare.parse(jsonStr, User.class); 

JSONの生成

OutputStreamに流す方法とStringにする方法があります。

OutputStreamへ

LoganSquare.serialize(user, os);

Stringへ

String jsonStr = LoganSquare.serialize(user);

その他

フィールド名の指定

モデルのフィールド名とJSONのフィールド名が違う場合、 @JsonFieldname オプションを指定することができます。

{"_id": 12345}

といったJSONのフィールド名 _id で、モデルでのフィールド名が id と定義していた場合

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away