SR Office

ブログ
2013年4月5日

Gsonでjava.lang.StackOverflowError




Google App Engine(GAE)での永続化はJDO(Java Data Objects)で行われる。作成したモデルはPOJOとして使えるのでモデルがきれいに使える。オブジェクト永続化の情報はアノテーションを使って指定する。

親オブジェクトが子オブジェクトを複数もつようなモデルを永続化するときは、アノテーションで1対Nの関係を指定しなければならない。

//親クラス
@PersistenceCapable
public class Parent {

 @PrimaryKey
 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
 @Extension(vendorName="datanucleus", key="gae.pk-id", value="true")
 private Long keyId;
 
 @Persistent(mappedBy = "parent")
 private List children;

 public Parent()
 {
 }
}

//子クラス
@PersistenceCapable
public class Child {

 @PrimaryKey
        @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
 private Key key;
  
 @Persistent
 private Parent parent;
 
 public Child()
 {  
 }
}

コードで表すとこうなる。Childクラスには親インスタンスをもつプロパティを作り、関係性を持たせる。
これをJDOで永続化すると1対Nの関係性をもつオブジェクトがそのまま保持される。

Webアプリの通信をJSONで行う場合、この親オブジェクトからJSON化していけば、綺麗にシリアライズされるはず。そう思ってこんなコードを書いた。ライブラリはGsonを使ってみた。


Gson gson = new Gson();
gson.toJson(parent);

シンプルだ。これでJSON化できるのだから簡単だ。
と思ったら、java.lang.StackOverflowErrorで落ちた。

もう、分かってると思うが、Gsonは親クラスをシリアライズした後、子クラスをシリアライズする。そのとき、マッピング用に指定している親オブジェクトもシリアライズしてしまう。するとまた親から子に向けてシリアライズを行い、永久ループに入ってしまう。

分かると単純なんだが、原因が分かるのにちょっと時間を使った。以前Hibernateでも同じように悩んだ記憶がある。同じ事を繰り返さないように記事にしておいた。