Graph processing capabilities enable serialization of complex object graphs containing references, shared objects, and circular dependencies. Standard Protocol Buffer format cannot handle object references, but protostuff-core provides specialized utilities for these advanced scenarios.
Main utility class for serializing and deserializing object graphs with reference tracking and cycle detection.
public final class GraphIOUtil {
public static <T> byte[] toByteArray(T root, Schema<T> schema, LinkedBuffer buffer);
public static <T> void writeTo(OutputStream out, T root, Schema<T> schema, LinkedBuffer buffer) throws IOException;
public static <T> void writeTo(DataOutput out, T root, Schema<T> schema, LinkedBuffer buffer) throws IOException;
public static <T> void mergeFrom(byte[] data, T message, Schema<T> schema) throws IOException;
public static <T> void mergeFrom(InputStream in, T message, Schema<T> schema) throws IOException;
public static <T> void mergeFrom(DataInput in, T message, Schema<T> schema) throws IOException;
}// Object graph with circular references
Person john = new Person("John");
Person jane = new Person("Jane");
john.spouse = jane;
jane.spouse = john; // circular reference
// Serialize the graph
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
byte[] graphData = GraphIOUtil.toByteArray(john, personSchema, buffer);// Deserialize maintaining object references
Person deserializedJohn = personSchema.newMessage();
GraphIOUtil.mergeFrom(graphData, deserializedJohn, personSchema);
// Verify circular reference is preserved
assert deserializedJohn.spouse.spouse == deserializedJohn;// Graph with shared objects
Company company = new Company("TechCorp");
Person employee1 = new Person("Alice", company);
Person employee2 = new Person("Bob", company);
// Both employees reference the same company instance
assert employee1.company == employee2.company;
// Serialize preserving shared reference
byte[] data = GraphIOUtil.toByteArray(Arrays.asList(employee1, employee2), listSchema, buffer);Wraps any CodedInput to add graph processing capabilities.
public final class GraphCodedInput extends CodedInput {
public GraphCodedInput(CodedInput input);
}Features:
Usage:
ByteArrayInput baseInput = new ByteArrayInput(graphData, false);
GraphCodedInput graphInput = new GraphCodedInput(baseInput);
// Use with schema for graph deserialization
schema.mergeFrom(graphInput, message);Specialized byte array input optimized for graph processing.
public final class GraphByteArrayInput extends ByteArrayInput {
public GraphByteArrayInput(byte[] buffer, boolean decodeNestedMessageAsGroup);
}Combines the efficiency of ByteArrayInput with built-in graph processing capabilities.
Output implementation that tracks object references during serialization.
public final class GraphProtostuffOutput implements Output {
public GraphProtostuffOutput(LinkedBuffer buffer);
}Features:
Internal Process:
Complex Domain Models:
// E-commerce domain with bidirectional relationships
Customer customer = new Customer("John Doe");
Order order = new Order(customer);
customer.orders.add(order); // bidirectional reference
// Graph serialization preserves relationships
byte[] data = GraphIOUtil.toByteArray(customer, customerSchema, buffer);Shared Configuration Objects:
// Multiple services sharing configuration
DatabaseConfig dbConfig = new DatabaseConfig("localhost", 5432);
UserService userService = new UserService(dbConfig);
OrderService orderService = new OrderService(dbConfig);
// Serialize service cluster
ServiceCluster cluster = new ServiceCluster(userService, orderService);
byte[] clusterData = GraphIOUtil.toByteArray(cluster, clusterSchema, buffer);Object Hierarchies with Cross-References:
// Organization chart with cross-department references
Department engineering = new Department("Engineering");
Department marketing = new Department("Marketing");
Employee manager = new Employee("Alice", engineering);
Project project = new Project("Launch", manager, Arrays.asList(engineering, marketing));
// Complex relationships preserved
byte[] orgData = GraphIOUtil.toByteArray(project, projectSchema, buffer);Simple Tree Structures:
Performance-Critical Scenarios:
Cross-Platform Compatibility:
The graph format extends standard protostuff encoding with:
// First occurrence: full object definition
field_id: 1, object_id: 100, data: { name: "John", age: 30 }
// Later reference: just the ID
field_id: 2, reference_id: 100Graph processing can throw additional exceptions:
// Custom exceptions for graph processing
public class GraphException extends RuntimeException {
public GraphException(String message);
public GraphException(String message, Throwable cause);
}Common Error Scenarios:
Buffer Sizing:
// Use larger buffers for complex graphs
LinkedBuffer buffer = LinkedBuffer.allocate(64 * 1024); // 64KBSchema Design:
// Minimize deep nesting in graph schemas
// Use flat structures with references when possibleSelective Graph Processing:
// Only use graph utilities for objects that actually need it
if (hasCircularReferences(object)) {
GraphIOUtil.toByteArray(object, schema, buffer);
} else {
ProtostuffIOUtil.toByteArray(object, schema, buffer);
}