Complete graph serialization and deserialization functionality for persistence, network transfer, and interoperability with other systems.
Converts graph instances to JSON-serializable objects preserving all graph data and metadata.
/**
* Converts graph to JSON-serializable object
* @param g - Graph instance to serialize
* @returns Object containing complete graph representation
*/
function write(g: Graph): SerializedGraph;
interface SerializedGraph {
options: {
directed: boolean;
multigraph: boolean;
compound: boolean;
};
nodes: SerializedNode[];
edges: SerializedEdge[];
value?: any; // Graph-level value if set
}
interface SerializedNode {
v: string; // Node ID
value?: any; // Node value/label if set
parent?: string; // Parent node ID (compound graphs only)
}
interface SerializedEdge {
v: string; // Source node ID
w: string; // Target node ID
name?: string; // Edge name (multigraphs only)
value?: any; // Edge value/label if set
}Usage Examples:
import { Graph, json } from "graphlib";
// Create and populate graph
const g = new Graph({ compound: true });
g.setGraph({ title: "Sample Graph", version: "1.0" });
// Add hierarchical nodes
g.setNode("group1", { type: "container" });
g.setNode("item1", { name: "First Item", data: 42 });
g.setNode("item2", { name: "Second Item", data: 73 });
g.setParent("item1", "group1");
g.setParent("item2", "group1");
// Add edges with metadata
g.setEdge("item1", "item2", {
relationship: "depends_on",
strength: 0.8
});
// Serialize to JSON
const serialized = json.write(g);
console.log(JSON.stringify(serialized, null, 2));Output format:
{
"options": {
"directed": true,
"multigraph": false,
"compound": true
},
"nodes": [
{
"v": "group1",
"value": { "type": "container" }
},
{
"v": "item1",
"value": { "name": "First Item", "data": 42 },
"parent": "group1"
},
{
"v": "item2",
"value": { "name": "Second Item", "data": 73 },
"parent": "group1"
}
],
"edges": [
{
"v": "item1",
"w": "item2",
"value": { "relationship": "depends_on", "strength": 0.8 }
}
],
"value": { "title": "Sample Graph", "version": "1.0" }
}Creates graph instances from JSON objects, restoring all graph properties and data.
/**
* Creates graph from JSON object
* @param json - Serialized graph object (format from write())
* @returns New Graph instance with restored data
*/
function read(json: SerializedGraph): Graph;Usage Examples:
// Restore graph from JSON
const restoredGraph = json.read(serialized);
// Verify restoration
console.log(restoredGraph.isCompound()); // true
console.log(restoredGraph.graph()); // { title: "Sample Graph", version: "1.0" }
console.log(restoredGraph.nodes()); // ["group1", "item1", "item2"]
console.log(restoredGraph.parent("item1")); // "group1"
console.log(restoredGraph.edge("item1", "item2"));
// { relationship: "depends_on", strength: 0.8 }// Save graph to file/database
const saveGraph = (graph, filename) => {
const serialized = json.write(graph);
const jsonString = JSON.stringify(serialized, null, 2);
// Save jsonString to file or database
return jsonString;
};
// Load graph from file/database
const loadGraph = (jsonString) => {
const parsed = JSON.parse(jsonString);
return json.read(parsed);
};// Client-side: send graph to server
const sendGraphToServer = async (graph) => {
const payload = json.write(graph);
const response = await fetch('/api/graphs', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
return response.json();
};
// Server-side: receive and process graph
const processReceivedGraph = (jsonPayload) => {
const graph = json.read(jsonPayload);
// Process graph...
const result = analyzeGraph(graph);
// Send result back
return json.write(result);
};// Create diff between graph versions
const createGraphDiff = (oldGraph, newGraph) => {
const oldSerialized = json.write(oldGraph);
const newSerialized = json.write(newGraph);
return {
old: oldSerialized,
new: newSerialized,
timestamp: Date.now()
};
};
// Apply updates to existing graph
const updateGraph = (currentGraph, updates) => {
// Serialize current state
let current = json.write(currentGraph);
// Apply node updates
updates.nodes?.forEach(nodeUpdate => {
const existingNode = current.nodes.find(n => n.v === nodeUpdate.v);
if (existingNode) {
Object.assign(existingNode, nodeUpdate);
} else {
current.nodes.push(nodeUpdate);
}
});
// Apply edge updates
updates.edges?.forEach(edgeUpdate => {
const existingEdge = current.edges.find(e =>
e.v === edgeUpdate.v && e.w === edgeUpdate.w && e.name === edgeUpdate.name
);
if (existingEdge) {
Object.assign(existingEdge, edgeUpdate);
} else {
current.edges.push(edgeUpdate);
}
});
return json.read(current);
};// Add version info to serialized graphs
const writeVersionedGraph = (graph, version = "1.0") => {
const serialized = json.write(graph);
return {
...serialized,
formatVersion: version,
timestamp: new Date().toISOString()
};
};
// Handle different format versions
const readVersionedGraph = (data) => {
const version = data.formatVersion || "1.0";
switch (version) {
case "1.0":
return json.read(data);
case "2.0":
// Handle newer format
return json.read(migrateFromV2(data));
default:
throw new Error(`Unsupported format version: ${version}`);
}
};// Convert to other graph formats
const convertToD3Format = (graph) => {
const serialized = json.write(graph);
const nodes = serialized.nodes.map(node => ({
id: node.v,
...node.value
}));
const links = serialized.edges.map(edge => ({
source: edge.v,
target: edge.w,
...edge.value
}));
return { nodes, links };
};
// Convert from DOT format (conceptual)
const convertFromDot = (dotString) => {
const parsed = parseDotString(dotString); // External parser
const serialized = {
options: {
directed: parsed.directed,
multigraph: false,
compound: false
},
nodes: parsed.nodes.map(n => ({ v: n.id, value: n.attributes })),
edges: parsed.edges.map(e => ({
v: e.from,
w: e.to,
value: e.attributes
}))
};
return json.read(serialized);
};// Serialize only specific node/edge properties
const writeMinimal = (graph) => {
const full = json.write(graph);
return {
...full,
nodes: full.nodes.map(node => ({
v: node.v,
// Only keep essential properties
value: node.value ? {
id: node.value.id,
type: node.value.type
} : undefined
})),
edges: full.edges.map(edge => ({
v: edge.v,
w: edge.w,
name: edge.name,
// Only keep weight property
value: edge.value?.weight
}))
};
};// Custom serialization with data transformation
const writeWithTransform = (graph, transformer) => {
const serialized = json.write(graph);
if (transformer.nodes) {
serialized.nodes = serialized.nodes.map(transformer.nodes);
}
if (transformer.edges) {
serialized.edges = serialized.edges.map(transformer.edges);
}
if (transformer.graph && serialized.value) {
serialized.value = transformer.graph(serialized.value);
}
return serialized;
};
// Usage
const compressed = writeWithTransform(graph, {
nodes: (node) => ({
v: node.v,
d: compressNodeData(node.value) // Custom compression
}),
edges: (edge) => ({
v: edge.v,
w: edge.w,
w: compressEdgeWeight(edge.value) // Custom compression
})
});write() time complexity: O(V + E) where V = nodes, E = edgesread() time complexity: O(V + E)// For large graphs, consider chunked processing
const writeChunked = (graph, chunkSize = 1000) => {
const serialized = json.write(graph);
const chunks = [];
const nodes = serialized.nodes;
const edges = serialized.edges;
for (let i = 0; i < nodes.length; i += chunkSize) {
chunks.push({
type: 'nodes',
data: nodes.slice(i, i + chunkSize)
});
}
for (let i = 0; i < edges.length; i += chunkSize) {
chunks.push({
type: 'edges',
data: edges.slice(i, i + chunkSize)
});
}
return {
options: serialized.options,
value: serialized.value,
chunks
};
};JSON serialization errors are generally related to data types:
// Safe serialization with error handling
const safeWrite = (graph) => {
try {
const serialized = json.write(graph);
JSON.stringify(serialized); // Test serializability
return serialized;
} catch (error) {
console.error("Serialization failed:", error);
// Return minimal serializable version
return {
options: {
directed: graph.isDirected(),
multigraph: graph.isMultigraph(),
compound: graph.isCompound()
},
nodes: graph.nodes().map(v => ({ v })),
edges: graph.edges().map(e => ({ v: e.v, w: e.w, name: e.name }))
};
}
};