- Zig 100%
| examples | ||
| src | ||
| .gitignore | ||
| build.zig | ||
| build.zig.zon | ||
| LICENSE | ||
| README.md | ||
m_xml
An allocation-free library for reading/writing XML, and serializing and deserializing Zig data structures as XML. This library doesn't support the full XML 1.0 specification, only the subset I need for my use cases.
const std = @import("std");
const m_xml = @import("m_xml");
const Biography = struct {
date: []const u8,
content: []const u8,
pub const xml = m_xml.schema(@This(), .{
.name = "biography",
.fields = .{ .content = m_xml.text },
});
};
const Person = struct {
id: u64,
age: u8,
name: []const u8,
biography: Biography,
pub const xml = m_xml.schema(@This(), .{
.name = "person",
.fields = .{
.name = m_xml.element,
.biography = m_xml.element,
},
});
};
pub fn main() !void {
const XML = m_xml.Codec(.{ .depth_max = 2, .attributes_max = 2 });
const document =
\\<person id='42' age='36'>
\\ <name>Ada & Co</name>
\\ <biography date='1843'>Designed analytical machines.</biography>
\\</person>
;
var input = document.*;
const person = try XML.parse(&input, Person);
std.debug.print("{d}: {s}, age {d}\n", .{ person.id, person.name, person.age });
std.debug.print(
"{s}: {s}\n",
.{ person.biography.date, person.biography.content },
);
// Parsed strings borrow `input`, so serialization uses separate storage.
var output: [256]u8 = undefined;
const serialized = try XML.serialize(&output, &person, m_xml.Style.compact);
std.debug.print("{s}\n", .{serialized});
}
Installation
From your project's root, add m_xml to build.zig.zon:
zig fetch --save=m_xml git+https://github.com/MidasVanVeen/m_xml
Then expose the module to your executable or library in build.zig:
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const m_xml_dependency = b.dependency("m_xml", .{
.target = target,
.optimize = optimize,
});
const executable = b.addExecutable(.{
.name = "example",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
}),
});
executable.root_module.addImport("m_xml", m_xml_dependency.module("m_xml"));
Application code can now import the package with const m_xml = @import("m_xml");.
XML subset
m_xml targets a strict subset of the
XML 1.0 Fifth Edition. Every document accepted or
produced by the library must also be a valid XML 1.0 document.
The target subset includes:
- UTF-8 documents, with an optional UTF-8 BOM.
- One properly nested root element, child elements, and empty elements.
- XML 1.0 names and characters. Colons in element and attribute names are matched literally rather than namespace-resolved.
- Text and uniquely named attributes.
- CDATA sections, exposed by the reader as ordinary text events. The low-level writer can emit
CDATA and safely splits values containing
]]>across sections. - The five predefined entity references:
&,<,>,", and'. - XML comments in the low-level reader and writer. Typed deserialization ignores comments, so typed round trips do not preserve them.
The reader rejects otherwise valid XML 1.0 documents that use features outside this subset, including:
- XML declarations and encodings other than UTF-8.
- Namespace processing.
- Processing instructions.
- Decimal and hexadecimal character references.
- DTDs, custom entities, and external entities.
I simply don't need these features for my personal use cases, as I suspect many other projects also don't.
License
m_xml is available under the Mozilla Public License 2.0.
Allocation-free?
m_xml never allocates from the heap and does not require an allocator. The caller provides the
mutable input, output buffer, and destination value. Parsed strings borrow slices of the input, and
the parser may normalize that input in place.
Runtime-sized collections use m_xml.BoundedArrayList(T, capacity), whose storage is part of the
value itself. Reader and writer limits such as maximum nesting depth and attributes per element are
also fixed by the Codec configuration. Exceeding any configured capacity returns an error rather
than allocating more memory.
Toolchain
Development currently targets Zig 0.16.0 and has no dependencies beyond the Zig standard library.
Examples
The examples/ directory contains example usages of m_xml. Run an example with
zig build run-example-name from the repository root.