Skip to content

Releases

xmojo v0.2.1

Choose a tag to compare

@amarbarosys amarbarosys released this 04 Nov 14:37

xmojo-public v0.2.1

Released: 2026-11-04 · Commit: 223472d · Previous: v0.2.0

What it does

XML for Mojo: parse into your own structs (from_xml[T]), write them back (to_xml), stream big
documents record by record (Records), or walk a compact tree (parse -> Doc). Structs are
walked with Mojo's standard reflection module; no dependency beyond the standard library. Every gate run checks the
parser against CPython's expat on 4,030 generated, mutated and real documents. Library only,
Mojo 1.2.0, linux-64; pixi install and -I src, or the conda package from recipe.yaml.

Highlights

  • Parser fixes: a numeric character reference above U+10FFFF is an error, a DOCTYPE internal subset is skipped to its ]>, an element with 16 or more attributes hashes its names correctly.
  • Mixed content joins an element's text once, and namespace scoping no longer copies a dict per element.
  • len(doc) works: Doc is Sized.

Changes

Added

  • len(doc) is the element count.
  • Standard library layout: pyproject.toml, scripts/ci.sh, a quickstart, docs/api.md and docs/design.md.
  • CI, latest-release and license badges in the README.

Changed

  • Namespace scope is one scope with an undo log, not a dict copy per declaring element.
  • from_xml raises a plain Error, not DeserializationError; the docs say so.
  • The skill no longer mentions emberserde.

Fixed

  • A numeric character reference above U+10FFFF is an error.
  • A DOCTYPE internal subset is skipped to its closing ]>.
  • Integer fields reject values outside their dtype.
  • A Dict key that is not an XML name raises instead of writing markup.
  • Mixed content joins an element's text once, at its end tag.
  • Attribute names on tags with 16 or more attributes are hashed.

Verified

Check Result
ci-local at 223472d: the GitHub workflow in a clean Ubuntu 24.04 container, Mojo 1.2.0 PASS

Known limits

Upgrade / run

pixi install      # Mojo 1.2.0
# in your code: from emberserde import Rename  ->  from xmojo import Rename

Commits

  • 223472d fix(release): pixi.toml, pyproject.toml, recipe.yaml say 0.2.1
  • c0618ea build: standard library layout (pyproject, ci.sh, quickstart, api and design docs)
  • b58806c docs(skill): drop the emberserde mentions v0.2.0 made stale
  • 8398886 fix(tokenizer): hash attribute names on tags with 16 or more attributes
  • 0e2306b fix(dom): one namespace scope with an undo log, not a dict copy per declaring element
  • df0c2fc fix(dom): mixed content joins an element's text once, at its end tag
  • 06513ec fix(release): pixi.toml and recipe.yaml say 0.2.0, the latest tag
  • db6c94a docs: from_xml raises a plain Error, not DeserializationError
  • 139888f fix(dom): Doc conforms to Sized so len(doc) is the element count
  • cdd73cd fix(encode): a Dict key that is not an XML name raises instead of writing markup
  • d494fd5 fix(decode): integer fields reject values outside their dtype
  • 8c2b967 fix(tokenizer): DOCTYPE internal subset is skipped to its closing ]>
  • 0b47bad fix(tokenizer): numeric character reference above U+10FFFF is an error
  • 2471c5d docs: CI, latest-release and license badges

xmojo v0.2.0

Choose a tag to compare

@amarbarosys amarbarosys released this 29 Oct 22:11

xmojo v0.2.0

Released: 2026-10-29 · Commit: a76920f · Previous: v0.1.0

What it does

XML for Mojo: parse into your own structs (from_xml[T]), write them back (to_xml), stream big
documents record by record (Records), or walk a compact tree (parse -> Doc). Structs are
walked with Mojo's standard reflection module; no dependency beyond the standard library. Every gate run checks the
parser against CPython's expat on 4,030 generated, mutated and real documents. Library only,
Mojo 1.2.0, linux-64; pixi install and -I src, or the conda package from recipe.yaml.

Highlights

  • Builds on Mojo 1.2.0. v0.1.0 could not: its dependency emberserde 0.1.0 ships a file precompiled by Mojo 1.1.0 that 1.2.0 refuses.
  • No third-party dependency: decoding and encoding are xmojo's own, over Mojo's standard reflection module.
  • Same from_xml[T] / to_xml behaviour and the same modifiers (Rename, RenameAll, Skip, Defaulted, Field[..., extra_names=...], DenyUnknownFields, ArmName), now imported from xmojo.

Changes

Changed

  • Breaking: the modifiers import from xmojo instead of emberserde: from xmojo import Rename, Skip, Defaulted, Field, RenameAll, RenamePolicy, DenyUnknownFields, ArmName.
  • Breaking: decode and encode errors are plain Error values with the same text as before (for example missing field: id, xmojo: unknown element <extra>), not emberserde's DeserializationError / SerializationError.
  • Pin moves to Mojo 1.2.0 (pixi.toml, recipe.yaml, pixi.lock).

Removed

  • The emberserde dependency. A struct set up for EmberJson no longer decodes from XML through the shared framework; it still decodes through xmojo's own rules.

Verified

Check Result
pixi run gate on Mojo 1.2.0: unit tests, tool builds, expat differential (2 seeds), negative control, README snippets PASS: 35/35 tests, 2015/2015 twice, negative control 53/215 differ
Same gate on Mojo 1.1.0 with emberserde removed PASS, same counts
from_xml 80k items, same session, best of 5: v0.1.0 (1.1.0 + emberserde) vs v0.2.0 (1.2.0) 28.3-28.9 ms vs 28.3-28.9 ms; peak RSS 27.4 MB vs 24.7 MB

Known limits

  • Parsing 80k sibling elements is about 5% slower on Mojo 1.2.0 than on 1.1.0 (16.8 vs 17.7 ms, same session); the parser did not change.
  • Only unit Variant arms, scalar SIMD fields and String map keys, as in v0.1.0.

Upgrade / run

pixi install      # Mojo 1.2.0
# in your code: from emberserde import Rename  ->  from xmojo import Rename

Commits

  • a76920f docs: README for the own reflection layer and Mojo 1.2
  • 6dc218c build: Mojo 1.2.0
  • 0e9dad3 refactor!: own reflection layer, drop emberserde
  • cf11d2a docs: NOTICE names the owner and contact
  • f45bf4d publish-check: lint workflows with actionlint
  • 7bc529e publish-check: fail on workflow actions pinned to a deprecated runtime
  • f2b695d ci: actions/checkout v7 (node24)

xmojo 0.1.0

Choose a tag to compare

@amarbarosys amarbarosys released this 12 Oct 06:07

xmojo v0.1.0

Released: 2026-10-12 · Previous: first release in this repository

What it does

XML for Mojo: parse into your own structs (from_xml[T]), write them back (to_xml), stream big
documents record by record (Records), or walk a compact tree (parse -> Doc). Built on
emberserde, so a struct that decodes from JSON decodes from XML too. Every gate run checks the
parser against CPython's expat on 4,030 generated, mutated and real documents. Library only,
Mojo 1.1.0, linux-64; pixi install and -I src, or the conda package from recipe.yaml.

Highlights

  • Structs in and out: attributes, child elements, text, Optional, List, Dict and unit enums,
    with from_xml[T](to_xml(v, "r")) giving v back.
  • Conforming: XML 1.0 well-formedness and Namespaces in XML, including attribute namespaces,
    xml:space, line-end and attribute normalization; 0 disagreements with expat.
  • Fast and small: 1.1 MB parsed in 15.4 ms (CPython's C ElementTree: 29.9 ms), about 100 bytes
    per element; Records keeps one record's tree in memory instead of the document's.
  • emberserde's modifiers work (Rename, RenameAll, Skip, Defaulted, aliases,
    DenyUnknownFields), and an agent skill ships in skills/xmojo/ (Claude Code plugin
    marketplace layout, AGENTS.md for other agents).

Changes

First release in this repository. Public API:

  • from_xml[T](text, entities=...), to_xml(value, root).
  • Records(text, local).next() -> Optional[String].
  • parse(text, entities=...) -> Doc and the Doc accessors listed in README, "DOM".
  • html_entities() for opt-in HTML named references; XmlError for malformed input.
  • Mapping rules, XML handling and limits: README. Agent skill: skills/xmojo/SKILL.md.

Verified

Check Result
pixi run gate: 9 unit-test files (incl. the README example as assertions), tool builds, tools/oracle.py seeds 1 and 7 (2015 documents each) against expat, --negative control, every runnable Mojo snippet in README and the skill PASS: 35/35 tests, 2015/2015 on both seeds, negative control 53/215 differ, snippets 3/3
pixi run package (rattler-build 0.76.1): build, then every tests/test_*.mojo importing the installed xmojo.mojoc xmojo-0.1.0-hb0f4dca_0.conda, 9/9 files, 35/35 tests; LICENSE and NOTICE in the package
2 GiB guard, once by hand (6.3 GB, 4 s, so not in the gate) parse raises on a 2,147,483,663-byte input; Records walks it
Fresh clone of the published tree: pixi install in an empty directory, then pixi run gate and pixi run build PASS, clone tree identical to the published one

Known limits

  • No DTDs: an internal subset is skipped, so its entities are unknown and raise.
  • UTF-8 only: a declared encoding other than UTF-8 or US-ASCII raises.
  • parse and from_xml take up to 2 GiB per document (Int32 offsets); Records can walk larger
    inputs, each record up to 2 GiB, but the input must be in memory.
  • Records copies and parses each record on its own: 3.5 times slower than whole-document
    from_xml on 14-byte records.
  • to_xml: no namespaces, no pretty-printing; enum arms with payloads and tuples are unsupported
    in both directions.

Upgrade / run

pixi install
pixi run gate

Commits

  • xmojo 0.1.0: initial public commit.