.. image:: ./json2xml-hero.png :alt: json2xml hero
======== json2xml ========
.. image:: https://badge.fury.io/py/json2xml.svg .. image:: https://static.pepy.tech/personalized-badge/json2xml?period=total&units=international_system&left_color=blue&right_color=orange&left_text=Downloads :target: https://pepy.tech/project/json2xml
.. image:: https://github.com/vinitkumar/json2xml/actions/workflows/pythonpackage.yml/badge.svg .. image:: https://img.shields.io/pypi/pyversions/json2xml.svg .. image:: https://readthedocs.org/projects/json2xml/badge/?version=latest :target: https://json2xml.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status .. image:: https://codecov.io/gh/vinitkumar/json2xml/branch/master/graph/badge.svg?token=Yt2h55eTL2 :target: https://codecov.io/gh/vinitkumar/json2xml
json2xml is a Python library and CLI for converting JSON data into XML. It is designed for teams that need predictable XML output, Python-first ergonomics, and a faster native path when conversion speed matters.
Documentation: https://json2xml.readthedocs.io.
The library was initially dependent on the dict2xml project, but it has now been integrated into json2xml itself. This has led to cleaner code, the addition of types and tests, and overall improved performance.
Looking for a Go version? Check out json2xml-go _, a Go port of this library with identical features and a native CLI tool.
Why json2xml? ^^^^^^^^^^^^^
Use json2xml when you need:
- A Python library for turning dictionaries, strings, files, or API responses into XML
- A command-line tool for quick JSON-to-XML conversion from scripts and terminals
- Optional Rust acceleration with automatic fallback to the pure Python implementation
- XPath 3.1 compatible output when standards-friendly XML is required
- A small, focused project with tests, docs, benchmarks, and multiple native implementation experiments
Install the Python package:
.. code-block:: console
pip install json2xml
Use it from Python:
.. code-block:: python
from json2xml import json2xml from json2xml.utils import readfromstring
data = readfromstring('{"name": "Ada", "language": "Python"}') print(json2xml.Json2xml(data).to_xml())
Use it from the terminal:
.. code-block:: console
json2xml-py -s '{"name": "Ada", "language": "Python"}'
Install the accelerated path:
.. code-block:: console
pip install json2xml[fast]
Real-world Examples ^^^^^^^^^^^^^^^^^^^
Convert a JSON API response in Python when another system expects XML:
.. code-block:: python
from json2xml import json2xml
api_response = {"user": {"id": 7, "name": "Ada"}, "active": True} print(json2xml.Json2xml(api_response, pretty=False).to_xml().decode("utf-8"))
Output:
.. code-block:: xml
Convert a local JSON export from the shell:
.. code-block:: console
cat > orders.json <<'JSON' {"orders":[{"id":"A100","total":19.99},{"id":"A101","total":5.5}]} JSON json2xml-py --no-pretty --no-type orders.json
Output:
.. code-block:: xml
Convert stdin in a shell pipeline:
.. code-block:: console
printf '%s\n' '{"event":"deploy","status":"ok"}' | json2xml-py --no-pretty --no-type -
Output:
.. code-block:: xml
Performance Snapshot ^^^^^^^^^^^^^^^^^^^^
The optional Rust extension is the fastest path for Python callers because it avoids process startup overhead and falls back to pure Python when a feature is not supported natively.
.. list-table:: :header-rows: 1 :widths: 30 20 20 15
* - Test Case - Pure Python - Rust Extension - Speedup * - Small JSON (47 bytes) - 31.49µs - 0.55µs - 56.8x * - Medium JSON (3.2 KB) - 1.69ms - 16.15µs - 105.0x * - Large JSON (32 KB) - 17.97ms - 168.21µs - 106.8x * - Very Large JSON (323 KB) - 183.33ms - 1.42ms - 129.0x
See BENCHMARKS.md _ for the full Python, Rust, Go, and Zig comparison.
Project Roadmap ^^^^^^^^^^^^^^^
The next phase of json2xml is focused on making the project easier to adopt, benchmark, and contribute to:
- Improve examples for common API, file, and CLI workflows
- Add clearer contribution paths for docs, CLI polish, and benchmark coverage
- Keep comparing Python, Rust, Go, and Zig implementations with reproducible benchmarks
- Continue hardening the Rust extension fallback behavior across platforms
ROADMAP.md _ for more detail.
Architecture Diagram ^^^^^^^^^^^^^^^^^^^^
.. image:: ./diagram.png
Installation ^^^^^^^^^^^^
As a Library
.. code-block:: console
pip install json2xml
With Native Rust Acceleration (up to 129x faster)
For maximum performance, install the optional Rust extension:
.. code-block:: console
# Install json2xml with Rust acceleration pip install json2xml[fast] # Or install the Rust extension separately pip install json2xml-rs
The Rust extension provides 57-129x faster conversion compared to pure Python in the latest benchmark. It's automatically used when available, with seamless fallback to pure Python.
As a CLI Tool
The library includes a command-line tool `json2xml-py that gets installed automatically:
.. code-block:: console
pip install json2xml
# Now you can use it from the command line json2xml-py data.json json2xml-py -s '{"name": "John", "age": 30}' json2xml-py -u https://api.example.com/data.json
For CLI options, run json2xml-py --help.
Features ^^^^^^^^
json2xml supports the following features:
- Conversion from a json
string to XML - Conversion from a json
file to XML - Conversion from an API that emits json
data to XML - Compliant with the json-to-xml
function specification fromXPath 3.1_ - Command-line tool for easy conversion from the terminal
You can use the json2xml library in the following ways:
.. code-block:: python
from json2xml import json2xml from json2xml.utils import readfromurl, readfromstring, readfromjson
# Convert JSON data from a URL to XML data = readfromurl("https://api.publicapis.org/entries") print(json2xml.Json2xml(data).to_xml())
# Convert a JSON string to XML data = readfromstring( '{"login":"mojombo","id":1,"avatar_url":"https://avatars0.githubusercontent.com/u/1?v=4"}' ) print(json2xml.Json2xml(data).to_xml())
# Convert a JSON file to XML data = readfromjson("examples/licht.json") print(json2xml.Json2xml(data).to_xml())
URL reads accept only credential-free HTTP(S), reject redirects and non-public
destinations by default, pin public connections to their validated DNS address,
and stop after 10 MiB of encoded or decoded content. Gzip and deflate responses
honor Content-Length and are decoded incrementally; other content encodings
are rejected. Trusted library callers can opt into a private endpoint with the
boolean True or choose a smaller limit:
.. code-block:: python
data = readfromurl( "http://127.0.0.1:8000/data.json", allow_private_networks=True, max_response_bytes=1024 * 1024, )
Custom Wrappers and Indentation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By default, a wrapper all and compact output (pretty=False) are set. Pretty printing can be enabled explicitly:
Conversions also default to a nesting limit of 100, an item limit of 100,000,
and a 10 MiB XML output limit. Pass max_depth, max_items, or
max_output_bytes to choose smaller budgets for untrusted workloads.
.. code-block:: python
from json2xml import json2xml from json2xml.utils import readfromurl, readfromstring, readfromjson
data = readfromstring( '{"login":"mojombo","id":1,"avatar_url":"https://avatars0.githubusercontent.com/u/1?v=4"}' ) print(json2xml.Json2xml(data, wrapper="all", pretty=True).to_xml())
Outputs this:
.. code-block:: xml
Omit List item ^^^^^^^^^^^^^^
Assume the following json input
.. code-block:: json
{ "my_items": [ { "my_item": { "id": 1 } }, { "my_item": { "id": 2 } } ], "my_str_items": ["a", "b"] }
By default, items in an array are wrapped in
Default output:
.. code-block:: xml
However, you can change this behavior using the item_wrap property like this:
.. code-block:: python
from json2xml import json2xml from json2xml.utils import readfromurl, readfromstring, readfromjson
data = readfromstring('{"my_items":[{"my_item":{"id":1} },{"my_item":{"id":2} }],"my_str_items":["a","b"]}') print(json2xml.Json2xml(data, item_wrap=False).to_xml())
Outputs this:
.. code-block:: xml
Optional Attribute Type Support ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can also specify if the output XML needs to have type specified or not. Here is the usage:
.. code-block:: python
from json2xml import json2xml from json2xml.utils import readfromurl, readfromstring, readfromjson
data = readfromstring( '{"login":"mojombo","id":1,"avatar_url":"https://avatars0.githubusercontent.com/u/1?v=4"}' ) print(json2xml.Json2xml(data, wrapper="all", pretty=True, attr_type=False).to_xml())
Outputs this:
.. code-block:: xml
XPath 3.1 Compliance Options ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The library supports the optional xpath_format parameter which makes the output compliant with the json-to-xml function specification from XPath 3.1 _. When enabled, the XML output follows the standardized format defined by the W3C specification.
.. code-block:: python
from json2xml import json2xml from json2xml.utils import readfromstring
data = readfromstring( '{"login":"mojombo","id":1,"avatar_url":"https://avatars0.githubusercontent.com/u/1?v=4"}' ) # Use xpath_format=True for XPath 3.1 compliant output print(json2xml.Json2xml(data, xpath_format=True).to_xml())
The methods are simple and easy to use and there are also checks inside of code to exit cleanly in case any of the input(file, string or API URL) returns invalid JSON.
Development ^^^^^^^^^^^
This project uses modern Python development practices. Here's how to set up a development environment:
.. code-block:: console
# Create and activate virtual environment (using uv - recommended) uv venv source .venv/bin/activate # On Windows: .venv\Scripts\activate # Install dependencies uv pip install -e ".[dev]"
Running Tests and Checks
We provide several ways to run tests and quality checks:
Using Make (recommended):
.. code-block:: console
make test # Run tests with coverage make lint # Run linting with ruff make typecheck # Run type checking with ty make check-all # Run all checks (lint, typecheck, test)
Using tools directly:
.. code-block:: console
pytest --cov=json2xml --cov-report=term -xvs tests -n auto ruff check json2xml tests uvx ty check json2xml tests
Rust Extension Development
The optional Rust extension (json2xml-rs) provides up to 129x faster performance in the latest benchmark. To develop or build the Rust extension:
Prerequisites:
.. code-block:: console
# Install Rust (if not already installed) curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Install maturin (Rust-Python build tool) uv pip install maturin
Building the extension:
.. code-block:: console
# Development build (installs in current environment) cd rust uv pip install -e . # Or using maturin directly maturin develop --release # Production wheel build maturin build --release
Running Rust benchmarks:
.. code-block:: console
# After building the extension python benchmarks/benchmark_rust.py
The Rust code is in rust/src/lib.rs and uses PyO3 for Python bindings.
CLI Usage ^^^^^^^^^
The json2xml-py command-line tool provides an easy way to convert JSON to XML from the terminal.
Basic Examples
.. code-block:: console
# Convert a JSON file to XML json2xml-py data.json
# Convert with custom wrapper element json2xml-py -w root data.json
# Read JSON from string json2xml-py -s '{"name": "John", "age": 30}'
# Read from stdin cat data.json | json2xml-py -
# Output to file json2xml-py -o output.xml data.json
# Use XPath 3.1 format json2xml-py -x data.json
# Disable pretty printing and type attributes json2xml-py --no-pretty --no-type data.json
CLI Options
.. code-block:: text
Input Options: -u, --url string Read JSON from URL -s, --string string Read JSON from string [input-file] Read JSON from file (use - for stdin)
Output Options: -o, --output string Output file (default: stdout)
Conversion Options:
-w, --wrapper string Wrapper element name (default "all")
-r, --root Include root element (default true)
-p, --pretty Pretty print output (default false)
-t, --type Include type attributes (default true)
-i, --item-wrap Wrap list items in
Other Options: -v, --version Show version information -h, --help Show help message
Go Version ^^^^^^^^^^
A Go port of this library is available at json2xml-go _.
Install the Go CLI:
.. code-block:: console
go install github.com/vinitkumar/json2xml-go/cmd/json2xml@latest
The Go version provides the same features and a native compiled binary for maximum performance.
Rust Extension (PyO3) ^^^^^^^^^^^^^^^^^^^^^
For users who need maximum performance within Python, json2xml includes an optional native Rust extension built with PyO3:
.. code-block:: console
pip install json2xml[fast]
Rust vs Pure Python Performance:
.. list-table:: :header-rows: 1 :widths: 30 20 20 15
* - Test Case - Pure Python - Rust Extension - Speedup * - Small JSON (47 bytes) - 31.49µs - 0.55µs - 56.8x * - Medium JSON (3.2 KB) - 1.69ms - 16.15µs - 105.0x * - Large JSON (32 KB) - 17.97ms - 168.21µs - 106.8x * - Very Large JSON (323 KB) - 183.33ms - 1.42ms - 129.0x
Usage with Rust Extension:
.. code-block:: python
# Automatic backend selection (recommended) from json2xml.dicttoxml_fast import dicttoxml, get_backend
print(f"Using backend: {get_backend()}") # 'rust' or 'python' data = {"name": "John", "age": 30} xml_bytes = dicttoxml(data)
The dicttoxml_fast module automatically uses the Rust backend when available and falls back to pure Python for unsupported features (like xpath_format, xml_namespaces, or custom item_func).
Platform Support:
Pre-built wheels are available for:
- Linux (x86_64, aarch64)
- macOS (x86_64, arm64/Apple Silicon)
- Windows (x86_64)
Performance Benchmarks ^^^^^^^^^^^^^^^^^^^^^^
Comprehensive benchmarks comparing all implementations (Apple Silicon, macOS 26.4.1, Python 3.14.4, April 2026):
.. list-table:: :header-rows: 1 :widths: 22 18 15 15 15 15
* - Test Case - Python - Rust - Go - Zig - Best * - Small (47B) - 31.49µs - 0.55µs - 4.09ms - 2.02ms - Rust (56.8x) * - Medium (3.2KB) - 1.69ms - 16.15µs - 4.07ms - 2.09ms - Rust (105.0x) * - Large (32KB) - 17.97ms - 168.21µs - 4.10ms - 2.42ms - Rust (106.8x) * - Very Large (323KB) - 183.33ms - 1.42ms - 4.20ms - 5.12ms - Rust (129.0x)
Key Findings:
- Rust extension: 57-129x faster than Python, zero process overhead (best for Python users)
- Go CLI: 43.6x faster for very large files (300KB+), but has ~4ms startup overhead
- Zig CLI: 7.4x faster for large files and 35.8x faster for very large files, with ~2ms startup overhead
- Python library calls: Use
pip install json2xml[fast](Rust, up to 129x faster) - Large file CLI processing: Use json2xml-go
_ orjson2xml-zig_ depending on your workload - Pure Python required: Use
pip install json2xml
_.
Other Implementations
^^^^^^^^^^^^^^^^^^^^^
This library is also available in other languages:
- Rust:
json2xml-rs _ - up to 129x faster, Python extension via PyO3
Go: json2xml-go _ - 43.6x faster for very large files, native CLI
Zig: json2xml-zig Help and Support to maintain this project ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- You can sponsor my work for this plugin here: https://github.com/sponsors/vinitkumar/