A new approach to circular references

3,171 views
Skip to first unread message

Jesse Wilson

unread,
Jan 1, 2012, 11:19:47 AM1/1/12
to googl...@googlegroups.com
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.name = name;
      this.company = company;
    }
  }

  static class Company {
    String name;
    List<Employee> employees = new ArrayList<Employee>();

    Company(String name) {
      this.name = 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?

Marcel Bruch

unread,
Jan 2, 2012, 2:49:22 AM1/2/12
to googl...@googlegroups.com
serialization of object graphs would be an excellent and greatly appreciated feature. Yaml uses &id and *id to indicate references in their structure. SnakeYaml serializes them at their first occurrence (not on top of the document) which I liked quite much since it keeps the original structure. Does it make sense to bring GSON's graph serialization support a bit closer to YAML standards?

In any way, graph serialization is my second most wanted feature. The most wanted one is performance - which seems to be addressed quite successfully with 2.1. Looking forward to give it a try.

Ouba Mahamane

unread,
Jan 17, 2013, 5:21:43 AM1/17/13
to googl...@googlegroups.com, je...@swank.ca
Hi Jesse Wilson.
I've been using your type adapter and it works fine. Thanks for sharing!
But I was wondering if it is possible to make the serialization persistent from 2 calls?
ie If an object O is present in 2 different graphs, will it be given the same id (the hexadecimal index) when the 2 graphs are serialized with 2 different toJson calls? 

Ouba Mahamane

unread,
Jan 17, 2013, 5:26:37 AM1/17/13
to googl...@googlegroups.com, je...@swank.ca
-

Jesse Wilson

unread,
Jan 17, 2013, 7:44:51 PM1/17/13
to Ouba Mahamane, googl...@googlegroups.com

Ouba,

The easiest way to accomplish that would be to serialize all of those things in one larger containing object. You could put everything in a HashMap, for example.

If that doesn't work for you, you could replace the nextName() method with something of your own invention.

Cheers, Jesse,

Reply all
Reply to author
Forward
0 new messages