Graph serialization
Data structure
Graph theory
Data encoding
Serialization techniques

Graph serialization

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Graph serialization is the process of converting a graph into a storable or transferable representation and later reconstructing the same graph structure from that representation. The hard part is not writing bytes. It is preserving identity, connectivity, direction, weights, and cycles without losing meaning or duplicating nodes incorrectly.

Decide What the Graph Needs to Preserve

Before picking a format, decide which graph properties matter:

  • directed or undirected edges
  • node identifiers
  • edge weights or labels
  • node metadata
  • multiedges or self-loops

A tree is easy to serialize recursively because each child usually has one parent. A general graph is harder because a node may be reachable from multiple places, and cycles are common.

That is why graph serialization should usually be ID-based rather than pointer-recursive.

An Adjacency-List Format Is a Good Default

A common practical format is an adjacency list with explicit node IDs.

python
1import json
2
3graph = {
4    "nodes": [
5        {"id": "A", "value": 1},
6        {"id": "B", "value": 2},
7        {"id": "C", "value": 3}
8    ],
9    "edges": [
10        {"source": "A", "target": "B", "weight": 5},
11        {"source": "B", "target": "C", "weight": 2},
12        {"source": "C", "target": "A", "weight": 7}
13    ]
14}
15
16payload = json.dumps(graph)
17print(payload)

This works well because:

  • nodes have stable identities
  • edges refer to nodes by ID
  • cycles are natural, not special cases
  • node and edge metadata can be added cleanly

Reconstruct the Graph Explicitly

Deserialization is usually a two-step process:

  1. create node objects from the node list
  2. connect them using the edge list
python
1import json
2from dataclasses import dataclass, field
3
4@dataclass
5class Node:
6    id: str
7    neighbors: list = field(default_factory=list)
8
9payload = '{"nodes": [{"id": "A"}, {"id": "B"}], "edges": [{"source": "A", "target": "B"}]}'
10data = json.loads(payload)
11
12nodes = {item["id"]: Node(item["id"]) for item in data["nodes"]}
13for edge in data["edges"]:
14    nodes[edge["source"]].neighbors.append(nodes[edge["target"]])
15
16print(nodes["A"].neighbors[0].id)

This explicit reconstruction avoids the classic mistake of creating duplicate node instances when the same node is referenced more than once.

Why Naive Recursive Serialization Fails

If you serialize a graph by recursively embedding neighbor objects inside each node, you quickly hit trouble:

  • repeated nodes are duplicated
  • cycles can cause infinite recursion
  • identity is lost during reconstruction

That does not mean nested formats are impossible. It means they need reference semantics, such as node IDs or special reference markers, rather than raw object expansion everywhere.

Choose the Wire Format Based on the Use Case

JSON is easy to inspect and widely supported, so it is a strong default for application boundaries and debugging.

Binary formats may be better when:

  • the graph is large
  • bandwidth matters
  • both ends share a schema
  • performance is more important than readability

But the higher-level design question remains the same: how do you preserve graph identity and relationships?

Versioning Matters More Than People Expect

Serialized graphs often outlive one version of the software. If the schema changes later, old saved graphs must still be interpretable.

That is why it is wise to include version metadata in the payload.

python
1graph = {
2    "version": 1,
3    "nodes": [{"id": "A"}],
4    "edges": []
5}

Even a simple version number gives you a place to branch migration logic later.

Common Pitfalls

The most common mistake is serializing object references implicitly and losing node identity during deserialization.

Another mistake is using a tree-shaped format for a graph that contains cycles or shared nodes.

A third issue is forgetting to record edge direction, weights, or metadata that the application needs later.

Finally, do not treat serialization as only a storage problem. It is also a schema design problem, especially when graphs are exchanged between systems or persisted long term.

Summary

  • Graph serialization should preserve identity and connectivity, not just raw values.
  • Adjacency-list style formats with explicit node IDs are a practical default.
  • Deserialization usually works best in two passes: create nodes, then connect edges.
  • Naive recursive object expansion breaks down on cycles and shared nodes.
  • Pick JSON or a binary format based on readability, size, and interoperability needs.
  • Include schema or version information if the serialized graph may live for more than one software version.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.