JSON をパースして詰め替えるのが面倒
Android アプリ開発において Bundle が非常に便利な入れ物であることは 拙記事: Bundle で述べた。
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()
で取って欲しい。
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 で扱うとコードが短くなる。
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}]