yog_io/graphml

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.

Quick Start

import yog/model.{Directed}
import yog_io/graphml

// Create a simple graph
let graph =
  model.new(Directed)
  |> model.add_node(1, "Alice")
  |> model.add_node(2, "Bob")

let assert Ok(graph) = model.add_edge(graph, from: 1, to: 2, with: "friend")

// Serialize to GraphML
let xml = graphml.serialize(graph)

// Write to file
let assert Ok(Nil) = graphml.write("graph.graphml", graph)

// Read from file
let assert Ok(loaded) = graphml.read("graph.graphml")

Format Overview

GraphML is an XML-based format that supports:

References

Types

Attribute data type for GraphML attributes.

These types are compatible with Gephi, yEd, and other GraphML tools.

pub type AttributeType {
  StringType
  IntType
  FloatType
  DoubleType
  BooleanType
  LongType
}

Constructors

  • StringType

    String type - for text data

  • IntType

    Integer type - for whole numbers

  • FloatType

    Float type - for decimal numbers (32-bit)

  • DoubleType

    Double type - for decimal numbers (64-bit, preferred for Gephi)

  • BooleanType

    Boolean type - for true/false values

  • LongType

    Long type - for large whole numbers (64-bit)

A graph with string-based attributes for nodes and edges. This is the default format used by serialize and deserialize.

pub type AttributedGraph =
  model.Graph(
    dict.Dict(String, String),
    dict.Dict(String, String),
  )

Attributes for an edge as a dictionary of string key-value pairs.

pub type EdgeAttributes =
  dict.Dict(String, String)

Options for GraphML serialization.

pub type GraphMLOptions {
  GraphMLOptions(indent: Int, xml_declaration: Bool)
}

Constructors

  • GraphMLOptions(indent: Int, xml_declaration: Bool)

    Arguments

    indent

    XML indentation spaces (0 for no formatting)

    xml_declaration

    Include XML declaration

Attributes for a node as a dictionary of string key-value pairs.

pub type NodeAttributes =
  dict.Dict(String, String)

Typed attributes for edges - maps attribute name to (value, type) pairs. Use this for proper Gephi compatibility with numeric and boolean attributes.

pub type TypedEdgeAttributes =
  dict.Dict(String, #(String, AttributeType))

Typed attributes for nodes - maps attribute name to (value, type) pairs. Use this for proper Gephi compatibility with numeric and boolean attributes.

pub type TypedNodeAttributes =
  dict.Dict(String, #(String, AttributeType))

Values

pub fn default_options() -> GraphMLOptions

Default GraphML serialization options.

pub fn deserialize(
  xml: String,
) -> Result(
  model.Graph(
    dict.Dict(String, String),
    dict.Dict(String, String),
  ),
  String,
)

Deserializes a GraphML string to a graph.

This is a simplified version of deserialize_with for graphs where you want node data and edge data as string dictionaries containing all attributes.

Time Complexity: O(V + E)

Example

import yog_io/graphml

let xml = "..."
let assert Ok(graph) = graphml.deserialize(xml)

// Access node data
let node1_data = dict.get(graph.nodes, 1)  // Dict(String, String)
let label = dict.get(node1_data, "label")
pub fn deserialize_with(
  node_folder: fn(dict.Dict(String, String)) -> n,
  edge_folder: fn(dict.Dict(String, String)) -> e,
  xml: String,
) -> Result(model.Graph(n, e), String)

Deserializes a GraphML string into a graph with custom data mappers.

This function allows you to control how GraphML attributes are converted to your node and edge data types. Use deserialize for simple cases where you want node/edge data as string dictionaries.

Time Complexity: O(V + E)

Example

import gleam/dict
import yog/model.{Directed}
import yog_io/graphml

type Person {
  Person(name: String, age: Int)
}

let node_folder = fn(attrs: dict.Dict(String, String)) {
  let name = dict.get(attrs, "name") |> result.unwrap("")
  let age = dict.get(attrs, "age") |> result.unwrap("0") |> int.parse |> result.unwrap(0)
  Person(name, age)
}

let edge_folder = fn(attrs: dict.Dict(String, String)) {
  dict.get(attrs, "type") |> result.unwrap("")
}

let xml = "..."
let assert Ok(graph) = graphml.deserialize_with(node_folder, edge_folder, xml)
pub fn read(
  path: String,
) -> Result(
  model.Graph(
    dict.Dict(String, String),
    dict.Dict(String, String),
  ),
  String,
)

Reads a graph from a GraphML file.

Example

import yog_io/graphml

let assert Ok(graph) = graphml.read("graph.graphml")

// Access node data
import gleam/dict
for node in dict.to_list(graph.nodes) {
  let #(id, data) = node
  io.debug("Node " <> int.to_string(id) <> ": " <> data)
}
pub fn read_with(
  path: String,
  node_folder: fn(dict.Dict(String, String)) -> n,
  edge_folder: fn(dict.Dict(String, String)) -> e,
) -> Result(model.Graph(n, e), String)

Reads a graph from a GraphML file with custom data mappers.

Example

import gleam/dict
import yog_io/graphml

type Person {
  Person(name: String, age: Int)
}

let node_folder = fn(attrs) {
  Person(
    dict.get(attrs, "name") |> result.unwrap(""),
    dict.get(attrs, "age") |> result.unwrap("0") |> int.parse |> result.unwrap(0)
  )
}

let assert Ok(graph) = graphml.read_with("people.graphml", node_folder, fn(attrs) {
  dict.get(attrs, "weight") |> result.unwrap("0") |> int.parse |> result.unwrap(0)
})
pub fn serialize(graph: model.Graph(String, String)) -> String

Serializes a graph to a GraphML string.

This is a simplified version of serialize_with for graphs where node data and edge data are already strings. The string data is stored as a “label” attribute.

Time Complexity: O(V + E)

Example

import yog/model.{Directed}
import yog_io/graphml

let graph =
  model.new(Directed)
  |> model.add_node(1, "Alice")
  |> model.add_node(2, "Bob")

let assert Ok(graph) = model.add_edge(graph, from: 1, to: 2, with: "5")

let xml = graphml.serialize(graph)
// <?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">5</data></edge>
//   </graph>
// </graphml>
pub fn serialize_with(
  node_attr: fn(n) -> dict.Dict(String, String),
  edge_attr: fn(e) -> dict.Dict(String, String),
  graph: model.Graph(n, e),
) -> String

Serializes a graph to a GraphML string with custom attribute mappers.

This function allows you to control how node and edge data are converted to GraphML attributes. Use serialize for simple cases where node/edge data are strings.

Time Complexity: O(V + E)

Example

import gleam/dict
import yog/model.{Directed}
import yog_io/graphml

type Person {
  Person(name: String, age: Int)
}

type Connection {
  Connection(weight: Int, relation: String)
}

let graph =
  model.new(Directed)
  |> model.add_node(1, Person("Alice", 30))
  |> model.add_node(2, Person("Bob", 25))

let node_attrs = fn(p: Person) {
  dict.from_list([#("name", p.name), #("age", int.to_string(p.age))])
}

let edge_attrs = fn(c: Connection) {
  dict.from_list([#("weight", int.to_string(c.weight)), #("type", c.relation)])
}

let xml = graphml.serialize_with(node_attrs, edge_attrs, graph)
pub fn serialize_with_options(
  node_attr: fn(n) -> dict.Dict(String, String),
  edge_attr: fn(e) -> dict.Dict(String, String),
  options: GraphMLOptions,
  graph: model.Graph(n, e),
) -> String

Serializes a graph to a GraphML string with custom options.

pub fn serialize_with_types(
  node_attr: fn(n) -> dict.Dict(String, #(String, AttributeType)),
  edge_attr: fn(e) -> dict.Dict(String, #(String, AttributeType)),
  graph: model.Graph(n, e),
) -> String

Serializes a graph to GraphML with typed attributes for Gephi compatibility.

This function allows you to specify the GraphML data type for each attribute, which is essential for proper Gephi compatibility. Numeric attributes (Int, Float, Double) will be recognized correctly for visualizations, layouts, and filters.

Time Complexity: O(V + E)

Example

import gleam/dict
import gleam/int
import yog/model.{Directed}
import yog_io/graphml.{DoubleType, IntType, StringType}

type Person {
  Person(name: String, age: Int, score: Float)
}

let node_attrs = fn(p: Person) {
  dict.from_list([
    #("name", #(p.name, StringType)),
    #("age", #(int.to_string(p.age), IntType)),
    #("score", #(float.to_string(p.score), DoubleType)),
  ])
}

let edge_attrs = fn(weight: Int) {
  dict.from_list([
    #("weight", #(int.to_string(weight), DoubleType)),
  ])
}

let xml = graphml.serialize_with_types(node_attrs, edge_attrs, graph)
pub fn serialize_with_types_and_options(
  node_attr: fn(n) -> dict.Dict(String, #(String, AttributeType)),
  edge_attr: fn(e) -> dict.Dict(String, #(String, AttributeType)),
  options: GraphMLOptions,
  graph: model.Graph(n, e),
) -> String

Serializes a graph to GraphML with typed attributes and custom options.

pub fn write(
  path: String,
  graph: model.Graph(String, String),
) -> Result(Nil, simplifile.FileError)

Writes a graph to a GraphML file.

Example

import yog/model.{Directed}
import yog_io/graphml

let graph =
  model.new(Directed)
  |> model.add_node(1, "Start")
  |> model.add_node(2, "End")

let assert Ok(graph) = model.add_edge(graph, from: 1, to: 2, with: "connection")

let assert Ok(Nil) = graphml.write("mygraph.graphml", graph)
pub fn write_with(
  path: String,
  node_attr: fn(n) -> dict.Dict(String, String),
  edge_attr: fn(e) -> dict.Dict(String, String),
  graph: model.Graph(n, e),
) -> Result(Nil, simplifile.FileError)

Writes a graph to a GraphML file with custom attribute mappers.

Example

import gleam/dict
import yog/model.{Directed}
import yog_io/graphml

type Person {
  Person(name: String, age: Int)
}

let node_attrs = fn(p: Person) {
  dict.from_list([#("name", p.name), #("age", int.to_string(p.age))])
}

let graph =
  model.new(Directed)
  |> model.add_node(1, Person("Alice", 30))

let assert Ok(Nil) = graphml.write_with(
  "people.graphml",
  node_attrs,
  fn(e) { dict.from_list([#("type", e)]) },
  graph
)
pub fn write_with_types(
  path: String,
  node_attr: fn(n) -> dict.Dict(String, #(String, AttributeType)),
  edge_attr: fn(e) -> dict.Dict(String, #(String, AttributeType)),
  graph: model.Graph(n, e),
) -> Result(Nil, simplifile.FileError)

Writes a graph to a GraphML file with typed attributes for Gephi compatibility.

This function creates GraphML files with proper attribute types that work seamlessly with Gephi for visualizations, layouts, and statistical analysis.

Example

import gleam/dict
import gleam/int
import yog/model.{Directed}
import yog_io/graphml.{DoubleType, IntType, StringType}

type Person {
  Person(name: String, age: Int, score: Float)
}

let node_attrs = fn(p: Person) {
  dict.from_list([
    #("label", #(p.name, StringType)),
    #("age", #(int.to_string(p.age), IntType)),
    #("score", #(float.to_string(p.score), DoubleType)),
  ])
}

let assert Ok(Nil) = graphml.write_with_types(
  "people.graphml",
  node_attrs,
  fn(e) { dict.from_list([#("weight", #(e, DoubleType))]) },
  graph
)
Search Document