M_xml - An XML serializer/deserializer for zig

m_xml


m_xml is an allocation-free library for reading/writing XML, and serializing/deserializing Zig data structures as XML. It targets a strict subset of XML, meaning not all XML features are supported, but all documents supported by this library are also valid XML 1.0 documents.

Parsed strings borrow slices of the input, and the parser may normalize that input in place. That is to say the input buffer is mutated when deserializing. This, and other constraints on depth and attribute count, allow this library to use only static memory.

It supports deserializing tagged-unions, structs, fixed and bounded collections, optional and default fields and mixed text/element content.

Usage example

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 &amp; 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});
}
10 Likes

Nice library!

The reader parsing functions are so clean. I’m doing an allocation-free parser for a custom language for a template engine and it’s a messy if/else tokenizer. I should learn some things from this codebase.

1 Like

It always helps to think of it as a state machine :slight_smile: