T tomlkit·org
Inspect Formatter Validator Lint Stats Keys Query Convert TOMLJSON JSONTOML TOMLYAML YAMLTOML INITOML TOMLINI .envTOML TOML.env TOMLTS TOMLCSV CSVTOML TOMLXML TOML.properties .propertiesTOML Transform Sort keys Flatten Unflatten Redact Minify Generate Go struct Rust struct Python types JSON Schema Compare Diff Merge

TOML to XML

updated 20 August 2026

Turn a TOML document into XML: tables become nested elements, scalars become text nodes or attributes, and arrays get either a wrapper element or a repeated tag. Useful when the config has to feed something that only speaks XML — an old service, an XSLT pipeline, a Java tool.

well-formed XML 1.0

What this tool does

It parses TOML and writes well-formed XML. Each table becomes an element named after its key; each scalar becomes either a child element with a text node or an attribute on its parent, depending on the Scalars as setting. Arrays become a wrapper element holding repeated <item> children, or the parent tag repeated once per element — the shape most XML schemas actually use.

The intent it closes: "my config is TOML but this consumer only reads XML." That consumer is usually a Java or .NET service with an XML settings file, an XSLT transform, or a legacy import that predates JSON.

Names are sanitised so the output is always parseable: characters XML does not allow in a name are replaced with a hyphen, and a name that would start with a digit gets an underscore prefix. Text is escaped for &, <, and >; attribute values additionally escape the double quote.

When you'd reach for it

  • Feed a legacy service. The app reads settings.xml; your source of truth is TOML.
  • Run an XSLT transform. XSLT needs XML in; this is the shortest path from a config file to a stylesheet.
  • Generate a fixture. A quick way to produce realistic XML test input from a config you already have.
  • Compare across formats. Convert both sides to XML and diff them with an XML-aware tool.
  • Hand a config to a Java tool. Many still expect XML settings; TOML is nicer to maintain.

How it works

One pass over the parsed tree, with the two structural choices applied as it goes.

1. Parse the TOML

The document becomes a value tree — dotted keys nested, [[blocks]] as arrays, dates as datetime values. Invalid TOML stops here with the parser's message in the status bar.

2. Walk and name

The root element is whatever you typed. Every key below it becomes an element name, sanitised to XML's name rules. In Attributes mode, scalar keys are lifted onto the parent element as attributes and only tables and arrays become child elements — which produces far shorter output for flat configs.

3. Serialise values

Booleans print as true/false, integers and floats as written, datetimes in their TOML text form. Empty strings and empty tables collapse to a self-closing tag. Everything is escaped, so a value containing < or & cannot break the document.

Options reference

Root element

The single outer element every XML document needs. Name it after the consumer's schema — configuration, settings, project. It is sanitised the same way as any other name.

Scalars as

Child elements gives one element per value: verbose, but every value can carry its own children later and it maps cleanly back to TOML. Attributes puts scalars on the parent tag, which is compact and matches the style of most hand-written XML config formats.

Arrays / Item tag

Wrapper + item elements keeps the array visible as a container: <tags><item>a</item></tags>. Repeat the parent tag emits <tags>a</tags><tags>b</tags>, which is what XSD sequences with maxOccurs expect. The item tag name only applies in wrapper mode.

Indent / Declaration

Indentation is cosmetic — two spaces, four, or a tab. The XML declaration is worth keeping unless the output is being embedded inside another document, where a nested declaration would be an error.

Example

Input:

[server]
host = "0.0.0.0"
port = 8080

[[routes]]
path = "/health"

Output (elements, wrapper arrays):

<?xml version="1.0" encoding="UTF-8"?>
<config>
  <server>
    <host>0.0.0.0</host>
    <port>8080</port>
  </server>
  <routes>
    <item>
      <path>/health</path>
    </item>
  </routes>
</config>

Switch Scalars as to Attributes and the same input becomes <server host="0.0.0.0" port="8080"/> — one line instead of four, at the cost of not being able to give those values children later.

Limits and notes

  • Types are lost. XML text nodes are strings; the reader decides what 8080 means. Nothing here writes a schema, so pair it with your own XSD if types matter.
  • Namespaces are not generated. The output has no prefixes or xmlns declarations. Add them by hand or with a stylesheet if the consumer requires them.
  • Comments are dropped. The TOML parser does not carry them into the tree.
  • Round-tripping is lossy. XML back to TOML cannot know which text nodes were numbers. If you need the return trip, XML to JSON on the sibling site is a better starting point.

FAQ

Should scalars be elements or attributes?

Elements are the safer default: any value can later gain children or repeat. Attributes are more compact and read better for flat settings, but an attribute cannot hold structure, so a value that grows into a table forces a rewrite.

How are arrays of tables converted?

Each entry becomes one child. In wrapper mode you get <routes> containing an <item> per entry; in repeat mode you get a <routes> element per entry with no wrapper — the shape XSD sequences usually specify.

Are key names always valid XML?

Yes. Characters that XML forbids in a name are replaced with a hyphen, and a leading digit gets an underscore prefix, so the output always parses. If your keys are unusual, check the result — the mapping is not reversible.

Is the output pretty-printed?

Yes, with the indent you choose. If you need it minified, run the result through the XML minifier on xmltoolskit.org.

Does the file leave my browser?

No. Parsing and serialisation are both local, so configs with internal hostnames or credentials stay on your machine.