Serialization and de-serialization for deep object graphs (references and cyclic dependencies).


  • high-performance graph serialization
  • handles cyclic dependencies and polymorphic pojos (interface and abstract classes)


  • When a root message is serialized/deserialized, its nested messages should not contain references to it.


~~~proto message ClubFounder { optional string name = 1; optional Club club = 2; }

message Club { optional string name = 1; repeated Student student = 2; repeated Club partner_club = 3; }

message Student { optional string name = 1; repeated Club club = 2; } ~~~

You cannot directly serialize Club since it can potentially have a reference to itself.

The solution is to wrap the target message.

message ClubWrapper {
    optional Club club = 1;

On the other hand, you can serialize ClubFounder.

Schema<ClubFounder> schema = RuntimeSchema.getSchema(ClubFounder.class);
ClubFounder founder = new ClubFounder();

// fill with cyclic club and student objects

byte[] data = GraphIOUtil.toByteArray(founder, schema);

ClubFounder cf = new ClubFounder();
GraphIOUtil.mergeFrom(data, cf, schema);

// check if cf retains the graph

For a more complex example, take a look at

Note that if you have collection fields that can potentially be cyclic, you need to enable the system property below:



class Foo
    List<Bar> barList;
    List<Baz> anotherBarList; // if this points to barList, it will be cyclic.


Doing so treats the collection field as a standalone message (which is handled on cyclic graph serialization).

Source document: