PlayFramework 別のモデルと関連があるモデルにGsonのtoJsonが使えないとき
Gsonを使うとモデルをそのままJSON形式に変換したい時に、こんな形で一発変換できます。
Gson g = new Gson(); Person p = Person.findById(myId); g.toJson(p); // {"name": "Taro Yamada", "birthday": "1920/06/30"}
ただ、Personが別のモデルとManyToOneとかOneToManyなど、何らかの関連があるときには「gson circular reference error」でJSON形式に変換できません。最初はモデルを一度Hashに変換してからJSON形式に変換していたのですが、なにか違うのかもしれないと思い、回避する方法を探してみたところ、Exposeアノテーションを使う方法と、ExclusionStrategyを実装する方法の2つ方法がありました。
@Exposeを使う方法
http://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/annotations/Expose.html
モデルでJSON形式として含めたいプロパティだけにExposeアノテーションを付けます。
この場合はsecretだけtoJSONしても出てきません。
@Expose public String name; @Expose public String address; @OneToMany(mappedBy="person", cascade=CascadeType.ALL) public List<Hoge> secrets;
モデルの設定が終わったらGsonBuilderでexcludeFieldsWithoutExposeAnnotationを使ってGsonインスタンスを生成します。
Gson g = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
Person p = Person.findById(id);
renderJSON(g.toJson(p));
うまくいったと思ったのですが、idが出力されないので使えません。。
いろいろごにょごにょした結果、最終的にもうひとつの方法を使いました。
http://stackoverflow.com/questions/4802887/gson-how-to-exclude-specific-fields-from-serialization-without-annotations
ExclusionStrategyを実装します。
さきほどと同じく他のモデルと関連があるsecretsを対象から外します。
package models; import com.google.gson.ExclusionStrategy; import com.google.gson.FieldAttributes; public class PersonExclusionStrategy implements ExclusionStrategy { public boolean shouldSkipClass(Class<?> arg0) { return false; } public boolean shouldSkipField(FieldAttributes f) { return (f.getDeclaringClass() == Person.class && f.getName().equals("secrets")); } }
でGsonをインスタンス化するときにこのExclusionStrategyを使うように指定します。
Gson g = new GsonBuilder() .setExclusionStrategies(new PersonExclusionStrategy()) .create(); Person p = Contact.findById(id); renderJSON(g.toJson(p));
無事に変換できました。
その他ソース:
http://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/GsonBuilder.html#excludeFieldsWithoutExposeAnnotation()
https://sites.google.com/site/gson/gson-user-guide