77
80

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

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

77
80
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
77
80

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?