JSONObject を Bundle に変換する

# JSON をパースして詰め替えるのが面倒

Android アプリ開発において Bundle が非常に便利な入れ物であることは [拙記事: Bundle](http://qiita.com/kojionilk/items/138eea19dadb14997136) で述べた。
RecyclerView / ListView の Adapter に詰めるのも Intent の引数に渡すにも Bundle を使う。

一方 WebAPI を用いた開発だと JSON を受け取るタイプの API をコールしてアプリ側で JSON をパースして使いやすいように詰め替える処理を書く事が多い。それが面倒なので **GSON** や **Jackson** といった JSON パーサライブラリを用いることもあるだろう。だがこれらのライブラリは自前の JavaBeans 若しくは POJO を用意する事が前提になっている。JSON をパースしてくれるのは有難いがこの POJO を毎回用意するのが面倒だ。Bundle に直接詰め替えたい。

# 一発で変換可能なユーティリティメソッド

というわけで JSONObject を簡単に Bundle に変換する為のメソッドを書いてみた。JSONObject 内に JSONArray 若しくは JSONObject が存在する場合も再帰的に解釈する。`{"hoge": "fuga", "list": [{"a": 1}, {"b": 2}], "json": {"hoge": "fuga"}}` 的なものも OK ということ。JSONArray は `ArrayList<Bundle>` に変換されるので `Bundle#getParcelableArrayList()` で取って欲しい。

```java
class Utils {

    /**
     * JSONObject を Bundle に適切に変換して返す.
     * JSONArray は ArrayList<Bundle> に変換される.
     *
     * @param json JSONObject
     * @return Bundle
     */
    public static Bundle toBundle(final JSONObject json) {
        final Bundle bundle = new Bundle();
        final Iterator<String> iterator = json.keys();
        while (iterator.hasNext()) {
            final String key = iterator.next();
            if (json.isNull(key)) {
                bundle.putString(key, null);
                continue;
            }
            final Object value = json.opt(key);
            if (value instanceof JSONObject) {
                bundle.putBundle(key, toBundle((JSONObject) value));
            } else if (value instanceof JSONArray) {
                bundle.putParcelableArrayList(key, toBundle((JSONArray) value));
            } else if (value instanceof Boolean) {
                bundle.putBoolean(key, (boolean) value);
            } else if (value instanceof String) {
                bundle.putString(key, (String) value);
            } else if (value instanceof Integer) {
                bundle.putInt(key, (int) value);
            } else if (value instanceof Long) {
                bundle.putLong(key, (long) value);
            } else if (value instanceof Double) {
                bundle.putDouble(key, (double) value);
            }
        }
        return bundle;
    }

    /**
     * JSONArray を ArrayList<Bundle> に適切に変換して返す.
     *
     * @param array JSONArray
     * @return ArrayList<Bundle>
     */
    public static ArrayList<Bundle> toBundle(final JSONArray array) {
        final ArrayList<Bundle> bundles = new ArrayList<>();
        for (int i = 0, size = array.length(); i < size; i++) {
            bundles.add(toBundle(array.optJSONObject(i)));
        }
        return bundles;
    }
}
```

以下のような感じで一発でパースできる。Volley 等の HTTP リクエスト用のライブラリを使用しているなら `Request<Bundle>` 等と戻り値を Bundle にして中で JSONObject から Bundle にパースして UI 側で最初から Bundle で扱うとコードが短くなる。

```java
final JSONObject json;
try {
    json = new JSONObject("{\"hoge\": \"fuga\", \"hage\": null, \"long\": 9223372036854775808, \"double\": 2.4d, \"list\": [{\"a\": 1}, {\"b\": 2}]}");
} catch (JSONException e) {
    throw new AndroidRuntimeException(e);
}
Log.d("BUNDLE", Utils.toBundle(json).toString());
```

```
D/BUNDLE(30128): Bundle[{double=2.4, hage=null, hoge=fuga, list=[Bundle[{a=1}], Bundle[{b=2}]], long=9.223372036854776E18}]
```