PlayFramework
Jackson

jacksonを使ってjsonをデシリアライズする

More than 3 years have passed since last update.


最初に

play(java)でデータのプロセス間の通信をする際にデフォルトで入っているjacksonを使ってjsonでやりとりするようにしたところ、導入のところで少し引っかかった。


引っかかった点


オブジェクト内にクラスを定義するときはstaticなクラスにしないと落ちる

テストのためにクラス内に変換先のオブジェクトをpublic classで書いたりするとエラーになる。


App.java

    public class App extends Controller{

public static Result index() throws Exception{
ObjectNode node = Json.newObject();
Test test = Json.fromJson(node, Test.class);
return ok(node);
}
public class Test {
private String test;
public String getTest() {return test;}
public void setTest(String test) {this.test = test;}
}
}

結果

play.api.Application$$anon$1: Execution exception[[RuntimeException: org.codehaus.jackson.map.JsonMappingException: No suitable constructor found for type [simple type, class controllers.App$Test]: can not instantiate from JSON object (need to add/enable type information?) at [Source: N/A; line: -1, column: -1]]]

外に出せばOK。この辺はインナークラスの扱いを理解していれば言うまでもなさそう。


空入力のコンストラクタがないと怒られる

また、デシリアライズ先のオブジェクトに引数が空のコンストラクタを用意しないと怒られる。↓みたいなのは怒られる。エラーは↑と同じ。

何もコンストラクタを記述しなければ気にする必要はないから忘れてると地味にハマるかもしれない。


Test.java

    public class Test {

private String test;
public Test(String test){}
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
}


インスタンス化したObjectMapper以外の物を使うと超遅い

↓みたいなやり方もあるが超遅い。一桁くらい処理時間に違いがある。

    new ObjectMapper().readValue(json, Test.class);

Json.fromJson(json, Test.class);

処理速度を比較すると↓みたいな感じ。なんかの機能で大量のjson化されたオブジェクトをデシリアライズするとかやろうとすると顕著な差が出る。

ObjectMapper#readValue >> インスタンス化+ObjectMapper#readValue >= Json.fromJson

ObjectMapperは使い回しが十分に効くので、色んなところで使うようならそれらの親クラスを作ってそこでfinal staticで初期化したものを使ってしまうといいかも知れない。試したことはない。


余計な情報がjsonの中に含まれてると例外が飛んでくる

例えば↓みたいなのを動かすと"UnrecognizedPropertyException"を投げてくる。

非チェック例外なのでインタフェースの定義が変わって追加が入った場合サービスがまともに機能しなくなるリスクがある。


App.java

    public class App extends Controller{

public static Result index() throws Exception{
ObjectNode node = Json.newObject();
node.put("no", "");
Test test = Json.fromJson(node, Test.class);
return ok(node);
}
static class Test {
private String test;
public String getTest() {return test;}
public void setTest(String test) {this.test = test;}
}
}

対応法は↓のアノテーションを付けるだけ。デフォルトだとfalseとして扱われ、例外を投げる。

@JsonIgnoreProperties(ignoreUnknown=true)

変更後↓


App.java

    public class App extends Controller{

public static Result index() throws Exception{
ObjectNode node = Json.newObject();
node.put("no", "");
Test test = Json.fromJson(node, Test.class);
return ok(node);
}
@JsonIgnoreProperties(ignoreUnknown=true)
static class Test {
private String test;
public String getTest() {return test;}
public void setTest(String test) {this.test = test;}
}
}