quick-xml-to-json 0.3.0

High-performance conversion of XML to JSON
Documentation

quick-xml-to-json

Crates.io Documentation

High-performance XML to JSON converter built on top of quick-xml.

This crate provides a fast, memory-efficient way to convert XML documents to JSON format, leveraging quick-xml's high-performance XML parsing capabilities.

Features

  • High Performance: Built on quick-xml for maximum parsing speed
  • Zero-Copy Option: xml_to_json_from_slice borrows events directly from in-memory input
  • Streaming: Processes XML as a stream without building a DOM — peak heap stays ~2 MiB regardless of document size
  • Attribute Support: Preserves XML attributes in JSON output
  • Entity Handling: Resolves predefined entities and numeric character references
  • Error Handling: Comprehensive error types with thiserror

Installation

Add this to your Cargo.toml:

[dependencies]
quick-xml-to-json = "0.3.0"

Usage

Three entry points, chosen by input shape:

  • xml_to_json_from_slice(&[u8], impl Write) — fastest; use when the document is already in memory
  • xml_to_json_from_bufread(impl BufRead, impl Write) — use when the input is already buffered
  • xml_to_json(impl Read, impl Write) — wraps any reader in a BufReader for you

Basic Conversion

use quick_xml_to_json::xml_to_json_from_slice;

let xml = r#"<users count="3">
  <user age="40">Jane Doe</user>
  <user age="42">John Doe</user>
</users>"#;

let mut output = Vec::new();
xml_to_json_from_slice(xml.as_bytes(), &mut output)?;

// output now contains the JSON bytes
let json_string = String::from_utf8(output)?;
println!("{}", json_string);

JSON Output Format

The crate converts XML to JSON using a specific format that preserves structure:

  • Attributes are prefixed with @ (e.g., @id="value")
  • Text content is stored under the #t key
  • Child elements are stored under the #c key as an array

Example XML:

<root id="main">
  <child>Hello World</child>
  <empty attr="value" />
</root>

Becomes:

{
  "root": {
    "@id": "main",
    "#c": [
      {
        "child": {
          "#t": "Hello World"
        }
      },
      {
        "empty": {
          "@attr": "value"
        }
      }
    ]
  }
}

Text, Entities, and Whitespace Semantics

  • An element's character data is emitted as a single #t value, concatenated in document order. For mixed content (<a>pre <b/>post</a>), text before and after child elements is joined into one #t alongside #c.
  • Leading and trailing whitespace of an element's text is trimmed; interior whitespace (including newlines) is preserved exactly.
  • Predefined entities (&amp;, &lt;, &gt;, &quot;, &apos;) and numeric character references (&#65;, &#x41;) are resolved in both text and attribute values. Named entities that cannot be resolved (e.g. DTD-defined entities like &uuml;) pass through verbatim rather than erroring or being dropped. Malformed character references (e.g. &#xZZ;) are errors.
  • CDATA sections are ignored.

Performance

XML fixture files have been sourced from https://aiweb.cs.washington.edu/research/projects/xmltk/xmldata/www/repository.html.

Measured with criterion on a 12-core MacBook Pro M4:

Fixture Size xml_to_json_from_bufread xml_to_json_from_slice
SigmodRecord.xml ~500 KB 354 MiB/s 405 MiB/s
mondial-3.0.xml ~1.8 MB 416 MiB/s 429 MiB/s
orders.xml ~5.4 MB 393 MiB/s 466 MiB/s
nasa.xml ~25.1 MB 414 MiB/s 467 MiB/s

On synthetic documents of many small elements, the buffered path sustains 9.2–9.5 million elements per second and the slice path 10.0–10.4 million.

Memory

Peak heap allocated during conversion, measured with a counting allocator while streaming each fixture from file to a sink: 2.01 MiB for SigmodRecord, mondial, and orders; 2.11 MiB for the ~25 MB nasa.xml. The footprint is dominated by the 2 MiB output buffer and is independent of document size. Reproduce with:

cargo run --release --example peak_memory -- benches/fixtures/nasa.xml

Run benchmarks with:

cargo bench

Test Suite

Run the test suite with:

cargo test

Dependencies

  • quick-xml: High-performance XML parsing
  • simdutf8: SIMD-accelerated UTF-8 validation
  • thiserror: Custom error types

License

This project is licensed under the MIT License - see the LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a pull request.