Ootempl.Xml (ootempl v0.3.0)

XML manipulation utilities for working with Erlang's :xmerl library.

This module provides Elixir-friendly wrappers around :xmerl record structures, enabling type-safe XML operations for parsing and manipulating Office Open XML document XML.

Record Structures

Defines Elixir records for the following :xmerl types:

  • :xmlElement - XML element nodes
  • :xmlAttribute - XML attribute nodes
  • :xmlText - XML text content nodes

Parsing and Serialization

Parse XML strings into :xmerl record structures:

{:ok, doc} = Ootempl.Xml.parse("<root><child>text</child></root>")

Serialize :xmerl records back to XML strings:

{:ok, xml_string} = Ootempl.Xml.serialize(doc)

Round-trip parsing and serialization (useful for testing):

{:ok, xml_string} = Ootempl.Xml.round_trip("<root><child>text</child></root>")

All parsing and serialization functions handle XML namespaces correctly, which is essential for .docx files that use namespaces extensively (w:, r:, etc.).

Usage

To use the record macros in your code, you need to import them:

import Ootempl.Xml

element = xmlElement(name: :div, content: [])
name = element_name(element)  # Returns "div"

See the test suite for comprehensive usage examples.

Summary

Functions

Parses an XML string into :xmerl record structures.

Removes a list of nodes from an XML element.

Serializes :xmerl record structures back to an XML string.

Types

xml_attribute()

@type xml_attribute() :: tuple()

xml_element()

@type xml_element() :: tuple()

xml_node()

@type xml_node() :: xml_element() | xml_text()

xml_text()

@type xml_text() :: tuple()

Functions

parse(xml_string)

@spec parse(String.t()) :: {:ok, xml_element()} | {:error, term()}

Parses an XML string into :xmerl record structures.

Uses :xmerl_scan.string/2 with namespace conformant parsing to correctly handle .docx XML which uses namespaces extensively (w:, r:, etc.).

Returns {:ok, document} on success, or {:error, reason} if parsing fails.

Examples

iex> {:ok, _doc} = Ootempl.Xml.parse("<root><child>text</child></root>")

iex> {:error, _reason} = Ootempl.Xml.parse("<root><unclosed>")

remove_nodes(element, nodes_to_remove)

@spec remove_nodes(xml_element(), [xml_node()]) :: xml_element()

Removes a list of nodes from an XML element.

Traverses the element's content and removes all nodes that match any node in the nodes_to_remove list. This is useful for removing conditional sections when conditions are false.

Parameters

  • element - The XML element to process
  • nodes_to_remove - List of nodes to remove from the element

Returns

The modified XML element with the specified nodes removed.

Examples

iex> {:ok, doc} = Ootempl.Xml.parse("<root><p>keep</p><p>remove</p></root>")
iex> [_keep, remove] = Ootempl.Xml.find_elements(doc, :p)
iex> modified = Ootempl.Xml.remove_nodes(doc, [remove])
iex> Ootempl.Xml.find_elements(modified, :p) |> length()
1

serialize(xml_record)

@spec serialize(xml_element()) :: {:ok, String.t()} | {:error, term()}

Serializes :xmerl record structures back to an XML string.

Uses :xmerl.export_simple/2 to convert the internal representation back to XML, preserving namespaces and attributes.

Returns {:ok, xml_string} on success, or {:error, reason} if serialization fails.

Examples

iex> {:ok, doc} = Ootempl.Xml.parse("<root><child>text</child></root>")
iex> Ootempl.Xml.serialize(doc)
{:ok, "<?xml version=..."}

xmlAttribute(args \\ [])

(macro)

xmlAttribute(record, args)

(macro)

xmlElement(args \\ [])

(macro)

xmlElement(record, args)

(macro)

xmlText(args \\ [])

(macro)

xmlText(record, args)

(macro)