Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Play Framework - JSON Serialization Cyclic References (e.g. Files->User, User->Files) --> Stackoverflow

I've been playing around with the play framework for a couple of days now but came across one issue I can't solve: Suppose you have some "User" and "File" classes. Both reference each other and are persisted in the database with ebean (@ManyToOne, @OneToMany). Now when return a user as JSON I get a stackoverflow Error. Is this a bug in play or am I missing something?

@Entity
public class File extends Model {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    public Long id;

    @ManyToOne(cascade=CascadeType.ALL)
    public User author;
}

@Entity
public class User extends Model {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    public Long id;

    @OneToMany(mappedBy="author", cascade=CascadeType.ALL)
    public List<File> files;
}

import play.libs.Json;
public class Users extends Controller {
   public static Result getJSON() {
        List<User> users = new Model.Finder<String, User>(String.class, User.class).all();
        return ok(Json.toJson(users));
    }
 }

Stacktrace:

org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: 
    models.User["files"]->com.avaje.ebean.common.BeanList[0]->models.File["author"]->
    models.User["files"]->com.avaje.ebean.common.BeanList[0]->models.File["author"]->
    models.User["files"]->com.avaje.ebean.common.BeanList[0]->models.File["author"]->
    models.User["files"]->com.avaje.ebean.common.BeanList[0]->models.File["author"]->
    models.User["files"]->com.avaje.ebean.common.BeanList[0]->models.File["author"]->
    models.User["files"]->com.avaje.ebean.common.BeanList[0]->models.File["author"]->
    models.User["files"]->com.avaje.ebean.common.BeanList[0]->models.File["author"]->
    models.User["files"]->com.avaje.ebean.common.BeanList[0]->models.File["author"]->
    .
    .
    .
    ->models.User["files"])
    at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:164) ~[jackson-mapper-asl.jar:1.9.10]
    at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112) ~[jackson-mapper-asl.jar:1.9.10]
    at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:446) ~[jackson-mapper-asl.jar:1.9.10]
    at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:150) ~[jackson-mapper-asl.jar:1.9.10]
    at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112) ~[jackson-mapper-asl.jar:1.9.10]
    at org.codehaus.jackson.map.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:72) ~[jackson-mapper-asl.jar:1.9.10]
 java.lang.StackOverflowError: null
    at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.7.0_45]
    at java.lang.ClassLoader.defineClass(ClassLoader.java:800) ~[na:1.7.0_45]
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) ~[na:1.7.0_45]
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:449) ~[na:1.7.0_45]
    at java.net.URLClassLoader.access$100(URLClassLoader.java:71) ~[na:1.7.0_45]
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361) ~[na:1.7.0_45]
like image 338
pasql Avatar asked Feb 17 '14 21:02

pasql


1 Answers

Okay, after hours of googling I found the solution: There is an annotation @JsonIgnore (import it from org.codehaus.jackson.annotate.JsonIgnore) which tells the JSON method to ignore this property.

EDIT: Digging further, the cleaner solution for dealing with cyclic references during JSON Serialization is to write a custom De- and Serializer. A very good post by Steve Hill can be found here: http://dev.sghill.net/2012/04/how-do-i-write-jackson-json-serializer.html

like image 163
pasql Avatar answered Sep 22 '22 11:09

pasql