Some Java application developers are still using Java serialization. For various reasons I've come to dislike Java serialization and I'd like to see it go extinct. For that to happen I need to make it easy for application developers to upgrade from Java serialization to JSON serialization.
Unfortunately there's a key feature in Java serialization that's lacking in JSON: cycles. Gson cannot bind instances that contain circular references. The fundamental problem is that a JSON document is a tree (no cycles) but Java objects form a graph (cycles permitted). We work around this by avoiding cycles in our object models, and this works relatively well.
So over the new years' holiday I put together a type adapter that can handle cycles. It isn't like other type adapters.
To illustrate how it works, here's a simple object model that permits a cycle.
static class Employee {
String name;
Company company;
Employee(String name, Company company) {
this.company = company;
}
}
static class Company {
String name;
List<Employee> employees = new ArrayList<Employee>();
Company(String name) {
}
}
public static void main(String... args) {
Company google = new Company("Google");
google.employees = Arrays.asList(
new Employee("Jesse", google),
new Employee("Joel", google)
);
...
The company refers to the employee and the employee refers back to that same company instance. This is bad news for writing out a tree! Here's what Gson will write before failing with a StackOverflowError:
{
"name": "Google",
"employees": [
{
"name": "Jesse",
"company": {
"name": "Google",
"employees": [
{
"name": "Jesse",
"company": {
"name": "Google",
"employees": [
{
"name": "Jesse",
"company": {
"name": "Google",
"employees": [
{
"name": "Jesse",
"company": {
"name": "Google",
"employees": [
In my new type adapter, we assign names like 0x1 to entities like google, jesse and joel. Whenever one entity refers to another, we substitute in the name. And we write the list of entities as the top-level document:
{
"0x1": {
"name": "Google",
"employees": [
"0x2",
"0x3"
]
},
"0x2": {
"name": "Jesse",
"company": "0x1"
},
"0x3": {
"name": "Joel",
"company": "0x1"
}
}
Configuration is a bit ugly at the moment because it registers a TypeAdapterFactory and an InstanceCreator for each type:
GsonBuilder gsonBuilder = new GsonBuilder()
.setPrettyPrinting();
new GraphAdapterBuilder()
.addType(Company.class)
.addType(Employee.class)
.registerOn(gsonBuilder);
Gson gson = gsonBuilder.create();
Is anyone interested in giving this a try?