LoginSignup
2
3

More than 5 years have passed since last update.

Realm Java + Jackson でintやStringのJSON配列をデシリアライズして保存する

Posted at

Realm Javaは(執筆時点のv.0.89.1では)intなどのプリミティブ型やStringのリストを扱えません。Realm Javaが扱えるのはRealmObjectとRealmListのみで、RealmListの要素はRealmObject(のサブクラス)である必要があります。つまり、以下のようなモデル定義はできません。

public class Hoge extends RealmObject {
    private RealmList<Integer> intList; // IntegerはRealmObjectじゃないのでエラー
    private RealmList<String>  strList; // StringはRealmObjectじゃないのでエラー

    // getter, setter...
}

これは、以下のようなJSONをGsonやJacksonなどで透過的にRealmObjectに変換できないという意味でもあります。

{
  "digit" : [1,2,3],
  "str"   : ["a","b","c"]
}

さすがにこの制限のためにJSON側を縛るのは非現実的です。Realm側でなんとかしましょう。

ラッパーを作る

まず、JSON配列で扱いたい値をRealmObjectでラッピングします。以下はintの例。

public class RealmInt extends RealmObject {
    private int val;

    public RealmInt() { }

    public RealmInt(int val) {
        this.val = val;
    }

    public int getVal() {
        return val;
    }

    public void setVal(int val) {
        this.val = val;
    }
}

Stringや他の型でも基本同じです。
※ フィールドがboolean型のときはgetterの名前に注意してください。Android Studioの補完機能でboolean型のgetterを自動生成するとisVal()のようにprefixがisとなります。getter経由でフィールドにアクセスするタイプのJSON Parserだと、isVal()ではなくgetVal()にしないと値がうまく取れないことがあります。

JSONデシリアライザを書く

上で作ったRealmIntは独自のクラスなので、JSON Parserに扱い方を教えてあげなければなりません。Gsonの場合は公式にサンプルへのリンクがあるのでこれが参考になります。ここではJackson(v.2.0.0)の例を載せます。

public class RealmIntArrayDeserializer extends JsonDeserializer<RealmList<RealmInt>> {
    @Override
    public RealmList<RealmInt> deserialize(JsonParser p, DeserializationContext ctxt)
            throws IOException, JsonProcessingException
    {
        if (!p.isExpectedStartArrayToken()) {
            throw new RuntimeJsonMappingException("Token does not start array.");
        }
        RealmList<RealmInt> intList = new RealmList<>();
        JsonToken token;
        do {
            token = p.nextToken();
            if (token == JsonToken.VALUE_NUMBER_INT) {
                intList.add(new RealmInt(p.getIntValue()));
            }
        } while (token != JsonToken.END_ARRAY);

        return intList;
    }
}

Stringの場合は JsonToken.VALUE_NUMBER_INTJsonToken.VALUE_STRING になります。ListではなくRealmListを返す点に注意してください。あとはモデル定義で、該当するフィールドにこのデシリアライザを使うようにアノテーションを付ければOKです。

public class Hoge extends RealmObject {
    @JsonDeserialize(using = RealmIntArrayDeserializer.class)
    private RealmList<RealmInt> digits;

    // getter, setter...
}

検証環境

  • Retrofit (v.2.0.0) でAPIからJSONをもらい、
  • Jackson (v.2.0.0) でJSONをパースし、
  • Realm (v.0.88.2) で保存しました。
2
3
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
2
3