JSONのパースにJacksonを使う

以前LDRFullFeedのJSONを扱うときに、1.5MBくらいのJSONを扱うとOut of Memoryになって苦労したけど、こういうのは Jackson を使うとよさげ。

Androidの標準のJSONパーサーは String しか渡せないけど、Jackson だと InputStream で渡せる。Stream で処理するのはちょっと面倒だけど、相当なメリットがありますな。


LDRMateのフィード一覧を読む部分をJacksonに変えた図。

JsonFactory factory = new JsonFactory();
JsonParser jp = factory.createJsonParser(is);
if (jp.nextToken() == JsonToken.START_ARRAY) {
    while (jp.nextToken() != JsonToken.END_ARRAY) {
        if (jp.getCurrentToken() == JsonToken.START_OBJECT) {
            long subscribeId = 0;
            String folder = "";
            String icon = "";
            String link = "";
            long modifiedOn = 0;
            int rate = 0;
            String title = "";
            int unreadCount = 0;
            
            while (jp.nextToken() != JsonToken.END_OBJECT) {
                String name = jp.getCurrentName();
                jp.nextToken();
                if ("subscribe_id".equals(name)) subscribeId = jp.getLongValue();
                else if ("icon".equals(name)) icon = jp.getText();
                else if ("folder".equals(name)) folder = jp.getText();
                else if ("link".equals(name)) link = jp.getText();
                else if ("modified_on".equals(name)) modifiedOn = jp.getLongValue();
                else if ("rate".equals(name)) rate = jp.getIntValue();
                else if ("title".equals(name)) title = jp.getText();
                else if ("unread_count".equals(name)) unreadCount = jp.getIntValue();
                else jp.skipChildren();
            }
            Feed feed = new Feed(subscribeId, folder, icon, link, 
                    modifiedOn, rate, title, unreadCount);
            feeds.add(feed);
        }
        else {
            jp.skipChildren();
        }
    }
}

Jacksonを使い始める前に簡単なベンチマークをとってみたけど、だいたい標準のJSONParserに比べて4倍くらいの速さですな。標準のJSONパーサーではStringに変換するために大きなメモリが必要になったり、いろんなところでオブジェクトが大量に生成されたりするけど、JacksonのStreamで処理すればそういう無駄がほとんど無くなるのも嬉しいですな。
LDReader で使われている org.json.simple も試したけどこれは800KB程度のJSONを読ませただで Out of Memory になる。標準のorg.jsonのパーサーでも処理できるサイズが処理できないのは不思議だけど、Jackson の結果がよかったので気にしないことにする...。


でもちょっと気になってることが。

{
  items: [ ... ],
  subscribe_id: 1
}

こういうデータを処理するとき、items を処理する段階で subscribe_id が必要だった場合。
Streamだと先にitemsを処理しなくちゃいけなくて、そのときは subscribe_id の情報がないから困ったことになる。
実際に今これで困ってるわけではないけど、Javascriptのオブジェクトの中身は順番が保証されないのに、読み出す側は特定の順番を期待して処理を書くのはまずいよなぁ...。