I'm struggling with Hibernate entities and JSON in these days and, although there is a lot of questions regarding the object, I'm yet not capable to serialize in presence of circular dependencies. I tried with both Gson and jackson but I didn't get a lot of progresses. Here is an excerpt from my objects. This is the "parent" class.
@Entity
public class User extends RecognizedServerEntities implements java.io.Serializable
{
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Integer id;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user", orphanRemoval = false)
@Cascade({CascadeType.SAVE_UPDATE})
private Set<Thread> threads = new HashSet<Thread>(0);
//...other attributes, getters and setters
}
and this is the "children" class
@Entity
@Table(name = "thread")
public class Thread extends RecognizedServerEntities implements java.io.Serializable
{
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Integer id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "author", nullable = true)
private User user;
//...other attributes, getters and setters
}
I've written a simple class to test both gson and jackson features; as said, they both raise an exception.
public class MyJsonsTest
{
private static User u;
public static void main(String[] args)
{
u = new User("mail", "password", "nickname", new Date());
u.setId(1); // Added with EDIT 1
// testGson();
testJackson();
}
private static void testJackson()
{
Thread t = new Thread("Test", u, new Date(), new Date());
t.setId(1); // Added with EDIT 1
u.getThreads().add(t);
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
try
{
mapper.writeValue(new File("result.json"), u);
}
catch {/[various exceptions catched, but a JsonMappingException was thrown]}
}
private static void testGson()
{
Gson gson = new Gson();
System.out.println(u.toString());
System.out.println(gson.toJson(u, User.class));
Thread t = new Thread("Test", u, new Date(), new Date());
u.getThreads().add(t);
//This raise an exception overflow
System.out.println(gson.toJson(u, User.class));
}
}
To solve the problem, on jackson side, I tried to use this annotation
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
on both User and Thread class. However, it doesn't solve the problem. On gson side, I read about the GraphAdapterBuilder class, but I wasn't able to properly use it. I don't find any jar, so I copy/pasted the source code from here. However, there is a compile time error at this line
private final ConstructorConstructor constructorConstructor = new ConstructorConstructor();
because the ConstructorConstructor() is undefined; the right syntax should be
ConstructorConstructor(Map<Type>, InstanceCreator<?> instanceCreators)
So, is there a definitive solution to this problem? Obviously, I can't use transient
variables.
EDIT 1
I finally found the issue with jackson. In the test class, I forgot to initialize the id field (in real scenarios it is initialized by the database) and this is the reason of the exception. When I finally set the id, all works. This is the output
{
"id" : 1,
"email" : "mail",
"password" : "password",
"nick" : "nickname",
"registeredDate" : 1414703168409,
"threads" : [ {
"id" : 1,
"thread" : null,
"user" : 1,
"title" : "Test",
"lastModifiedDate" : 1414703168410,
"createdDate" : 1414703168410,
"messages" : [ ],
"threads" : [ ]
} ],
"messages" : [ ]
}