json-simpleを使いやすく
ちょっとCassandraをいじっている。内部でJSONを使いたくなったので、Cassandraがもともと使っているjson-simpleを使ってみた。
Object o = (new JSONParser()).parse(jsonString);
のように使う。jsonのMapはJSONObjectに、Listは JSONArrayにマップされる。しかしこれでは使うときにいちいちcastしなければならないし、Parseされた構造が期待通りの構造になっているのかをいちいちチェックしなければならない。できればオブジェクトにマップしたい、ということで関数を書いてみた。
関数
第二引数にクラスを渡すと、そのクラスのインスタンスをつくって、そこにJSONをパースした結果を入れる。やっていることは、クラスのフィールド名をリフレクションで取得して、JSONから取り出して突っ込もうとするだけ。型が違ったりすると例外を投げる。
static Object unmarshal(Object obj, Class clazz) throws InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException { if (clazz == String.class) return (String)obj; if (clazz == Integer.class) return (Integer)obj; if (clazz == Long.class) return (Long)obj; if (clazz == List.class) return (JSONArray)obj; JSONObject jObj = (JSONObject)obj; Object o = clazz.newInstance(); for (Object s: jObj.keySet()) { Field f = clazz.getField((String)s); Object inner = unmarshal(jObj.get(s), f.getType()); f.set(o, inner); } return o; }
使い方
こんなJSONをパースすることを考える。
{ "name": "ExternalProcessUDF", "command" : ["a", "b"], "args" : { "saru" : 1 } }
これを下のようなネスト構造を持つオブジェクトにマップする。
class Inner { public Long saru; } class External { public String name; public List<String> command; public Inner args; }
これにはこのようにすればいい。
Object o = (new JSONParser()).parser.parse(json); External e = (External) unmarshal(o, External.class);
取り出す手間が省ける、というよりは構造がチェックできるということのほうが大きいかも。