GraphML (Graph Markup Language) serialization support.
Provides functions to serialize and deserialize graphs in the GraphML format, an XML-based format widely supported by graph visualization and analysis tools like Gephi, yEd, Cytoscape, and NetworkX.
Format Overview
GraphML is an XML-based format that supports:
- Nodes with custom attributes
- Edges with custom attributes
- Directed and undirected graphs
- Hierarchical graphs (not yet supported)
Performance
For optimal performance with large GraphML files, add the optional saxy
dependency to your project:
{:saxy, "~> 1.5"}When available, saxy provides a fast streaming SAX parser that significantly
improves loading times:
- Without saxy: Uses Erlang's
:xmerl(DOM parser, slower for large files) - With saxy: Uses streaming parser (up to 3-4x faster for large files)
For example, loading a 60MB GraphML file with ~500k edges:
- xmerl: ~20 seconds
- saxy: ~6 seconds
Examples
Basic Serialization and Deserialization
iex> graph = Yog.directed()
...> |> Yog.add_node(1, "Alice")
...> |> Yog.add_node(2, "Bob")
...> |> Yog.add_edge!(from: 1, to: 2, with: "friend")
iex> xml = Yog.IO.GraphML.serialize(graph)
iex> String.contains?(xml, "Alice")
true
iex> String.contains?(xml, "Bob")
trueCustom Attributes with Type Information
iex> graph = Yog.directed()
...> |> Yog.add_node(1, %{name: "Alice", age: 30})
...> |> Yog.add_node(2, %{name: "Bob", age: 25})
...> |> Yog.add_edge!(from: 1, to: 2, with: %{weight: 5, relation: "friend"})
iex> node_attr = fn data ->
...> %{"label" => data.name, "age" => Integer.to_string(data.age)}
...> end
iex> edge_attr = fn data ->
...> %{"weight" => Integer.to_string(data.weight), "type" => data.relation}
...> end
iex> xml = Yog.IO.GraphML.serialize_with(node_attr, edge_attr, graph)
iex> String.contains?(xml, "Alice")
trueReading from File
# Read a GraphML file from disk
{:ok, graph} = Yog.IO.GraphML.read("network.graphml")Writing to File
# Write with default string conversion
Yog.IO.GraphML.write("output.graphml", graph)Output Format
<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns">
<key id="label" for="node" attr.name="label" attr.type="string"/>
<key id="weight" for="edge" attr.name="weight" attr.type="string"/>
<graph id="G" edgedefault="directed">
<node id="1">
<data key="label">Alice</data>
</node>
<node id="2">
<data key="label">Bob</data>
</node>
<edge source="1" target="2">
<data key="weight">friend</data>
</edge>
</graph>
</graphml>References
Summary
Functions
Returns default GraphML serialization options.
Deserializes a GraphML string to a graph using default conversion.
Deserializes a GraphML string into a graph with custom data mappers.
Reads a graph from a GraphML file using default conversion.
Reads a graph from a GraphML file with custom data mappers.
Serializes a graph to GraphML string using default attribute conversion.
Serializes a graph to GraphML string with custom attribute mappers.
Serializes a graph to a GraphML string with custom options.
Serializes a graph to GraphML with typed attributes for Gephi compatibility.
Serializes a graph to GraphML with typed attributes and custom options.
Writes a graph to a GraphML file using default attribute conversion.
Writes a graph to a GraphML file with custom attribute mappers.
Writes a graph to a GraphML file with typed attributes for Gephi compatibility.
Functions
Returns default GraphML serialization options.
The options control XML formatting:
- indent: Number of spaces for indentation (default: 2)
- include_xml_declaration: Whether to include XML declaration (default: true)
Example
iex> {:graphml_options, indent, include_xml_declaration} = Yog.IO.GraphML.default_options()
iex> indent
2
iex> include_xml_declaration
true
Deserializes a GraphML string to a graph using default conversion.
Node and edge attributes are stored as-is in maps. For custom data structures,
use deserialize_with/3.
Time Complexity: O(V + E) where V is nodes and E is edges
Returns
{:ok, graph}on success{:error, {:parse_error, reason}}on parsing failure
Example
iex> xml = """
...> <?xml version="1.0" encoding="UTF-8"?>
...> <graphml xmlns="http://graphml.graphdrawing.org/xmlns">
...> <graph id="G" edgedefault="directed">
...> <node id="1"><data key="label">Alice</data></node>
...> <node id="2"><data key="label">Bob</data></node>
...> </graph>
...> </graphml>
...> """
iex> {:ok, graph} = Yog.IO.GraphML.deserialize(xml)
iex> Yog.Model.node_count(graph)
2Performance Note
For large files, parsing automatically uses the fast saxy parser when available,
falling back to :xmerl otherwise. See module documentation for performance details.
Deserializes a GraphML string into a graph with custom data mappers.
This function allows you to transform the raw GraphML attributes into custom Elixir data structures as the graph is built.
Time Complexity: O(V + E) where V is nodes and E is edges
Parameters
node_folder- Function to transform node attributes to node data(attrs :: map) -> node_dataedge_folder- Function to transform edge attributes to edge data(attrs :: map) -> edge_dataxml- The GraphML XML string to parse
Returns
{:ok, graph}on success{:error, {:parse_error, reason}}on parsing failure
Example
iex> xml = """
...> <?xml version="1.0" encoding="UTF-8"?>
...> <graphml xmlns="http://graphml.graphdrawing.org/xmlns">
...> <graph id="G" edgedefault="directed">
...> <node id="1">
...> <data key="name">Alice</data>
...> <data key="age">30</data>
...> </node>
...> <node id="2">
...> <data key="name">Bob</data>
...> <data key="age">25</data>
...> </node>
...> <edge source="1" target="2">
...> <data key="weight">5</data>
...> </edge>
...> </graph>
...> </graphml>
...> """
iex> node_folder = fn attrs ->
...> %{name: Map.get(attrs, "name"), age: String.to_integer(Map.get(attrs, "age", "0"))}
...> end
iex> edge_folder = fn attrs ->
...> String.to_integer(Map.get(attrs, "weight", "1"))
...> end
iex> {:ok, graph} = Yog.IO.GraphML.deserialize_with(node_folder, edge_folder, xml)
iex> Yog.Model.node_count(graph)
2
Reads a graph from a GraphML file using default conversion.
This is a convenience function that combines File.read/1 with deserialize/1.
Time Complexity: O(V + E) + file I/O
Parameters
path- File path to read from
Returns
{:ok, graph}on success{:error, reason}on file read or parse failure
Example
# Read a GraphML file exported from Gephi or NetworkX
{:ok, graph} = Yog.IO.GraphML.read("social_network.graphml")
# Check what we loaded
IO.puts("Nodes: #{Yog.Model.node_count(graph)}")
IO.puts("Edges: #{Yog.Model.edge_count(graph)}")
Reads a graph from a GraphML file with custom data mappers.
Use this when you want to transform GraphML attributes into custom Elixir data structures during loading.
Time Complexity: O(V + E) + file I/O
Parameters
path- File path to read fromnode_folder- Function to transform node attributes to node dataedge_folder- Function to transform edge attributes to edge data
Returns
{:ok, graph}on success{:error, reason}on file read or parse failure
Example
# Transform GraphML data to custom structs
node_folder = fn attrs ->
%Person{
name: Map.get(attrs, "name", "Unknown"),
score: String.to_integer(Map.get(attrs, "score", "0"))
}
end
edge_folder = fn attrs ->
%Relationship{
type: Map.get(attrs, "type", "knows"),
strength: String.to_float(Map.get(attrs, "strength", "0.5"))
}
end
{:ok, graph} = Yog.IO.GraphML.read_with(
"network.graphml",
node_folder,
edge_folder
)
Serializes a graph to GraphML string using default attribute conversion.
Node and edge data are converted to strings and stored as "label" and "weight"
attributes respectively. For custom attribute mapping, use serialize_with/3.
Time Complexity: O(V + E)
Example
iex> graph = Yog.directed()
...> |> Yog.add_node(1, "Alice")
...> |> Yog.add_node(2, "Bob")
...> |> Yog.add_edge!(from: 1, to: 2, with: "friend")
iex> xml = Yog.IO.GraphML.serialize(graph)
iex> String.contains?(xml, ~s(<node id="1">)) and String.contains?(xml, "Alice")
true
Serializes a graph to GraphML string with custom attribute mappers.
This is the main serialization function allowing you to control how node and edge data are converted to GraphML attributes.
Time Complexity: O(V + E) where V is the number of nodes and E is edges
Parameters
node_attr- Function that converts node data to a map of attributes(node_data) -> %{string => string}edge_attr- Function that converts edge data to a map of attributes(edge_data) -> %{string => string}graph- The graph to serialize
Example
iex> graph = Yog.directed()
...> |> Yog.add_node(1, %{name: "Alice", role: "admin"})
...> |> Yog.add_node(2, %{name: "Bob", role: "user"})
...> |> Yog.add_edge!(from: 1, to: 2, with: %{since: "2024"})
iex> node_attr = fn data ->
...> %{"label" => data.name, "role" => data.role}
...> end
iex> edge_attr = fn data ->
...> %{"since" => data.since}
...> end
iex> xml = Yog.IO.GraphML.serialize_with(node_attr, edge_attr, graph)
iex> String.contains?(xml, "Alice") and String.contains?(xml, "admin")
true
Serializes a graph to a GraphML string with custom options.
Serializes a graph to GraphML with typed attributes for Gephi compatibility.
Identical to serialize_with/3 but explicitly intended for tools like Gephi
that benefit from type information in the key definitions.
Time Complexity: O(V + E)
Example
iex> graph = Yog.directed()
...> |> Yog.add_node(1, "Node1")
...> |> Yog.add_node(2, "Node2")
iex> node_attr = fn data -> %{"label" => data} end
iex> edge_attr = fn _ -> %{} end
iex> xml = Yog.IO.GraphML.serialize_with_types(node_attr, edge_attr, graph)
iex> String.contains?(xml, ~s(attr.type="string"))
true
Serializes a graph to GraphML with typed attributes and custom options.
Provides full control over both attribute mapping and output formatting.
Time Complexity: O(V + E)
Parameters
node_attr- Function to convert node data to attributesedge_attr- Function to convert edge data to attributesoptions- GraphML options tuple (seedefault_options/0)graph- The graph to serialize
Example
iex> graph = Yog.directed() |> Yog.add_node(1, "A")
iex> node_attr = fn data -> %{"label" => data} end
iex> edge_attr = fn _ -> %{} end
iex> options = {:graphml_options, 4, false} # 4-space indent, no XML declaration
iex> xml = Yog.IO.GraphML.serialize_with_types_and_options(node_attr, edge_attr, options, graph)
iex> String.contains?(xml, "<?xml")
false
Writes a graph to a GraphML file using default attribute conversion.
This is a convenience function that combines serialize/1 with File.write/2.
Time Complexity: O(V + E) + file I/O
Parameters
path- File path to write tograph- The graph to serialize
Returns
{:ok, nil}on success{:error, reason}on file write failure
Example
graph = Yog.directed()
|> Yog.add_node(1, "Alice")
|> Yog.add_node(2, "Bob")
|> Yog.add_edge!(from: 1, to: 2, with: "friend")
Yog.IO.GraphML.write("network.graphml", graph)
# => {:ok, nil}
Writes a graph to a GraphML file with custom attribute mappers.
Use this when you need control over how node and edge data are converted to GraphML attributes.
Time Complexity: O(V + E) + file I/O
Parameters
path- File path to write tonode_attr- Function to convert node data to attributesedge_attr- Function to convert edge data to attributesgraph- The graph to serialize
Returns
{:ok, nil}on success{:error, reason}on file write failure
Example
graph = Yog.directed()
|> Yog.add_node(1, %{name: "Alice", score: 95})
|> Yog.add_node(2, %{name: "Bob", score: 87})
node_attr = fn data ->
%{
"label" => data.name,
"score" => Integer.to_string(data.score)
}
end
edge_attr = fn _ -> %{} end
Yog.IO.GraphML.write_with("network.graphml", node_attr, edge_attr, graph)
Writes a graph to a GraphML file with typed attributes for Gephi compatibility.
Identical to write_with/4 but explicitly intended for tools that require
type annotations.
Time Complexity: O(V + E) + file I/O
Example
graph = Yog.directed() |> Yog.add_node(1, "Node1")
node_attr = fn data -> %{"label" => data} end
edge_attr = fn _ -> %{} end
Yog.IO.GraphML.write_with_types("network.graphml", node_attr, edge_attr, graph)