Convert TOML to XML

Converting TOML to XML turns a manifest such as pyproject.toml or Cargo.toml into an element tree an older intake can read. Tables become nested elements and arrays of tables become repeated ones, which is a clean fit — but the root name, the attribute question and TOML’s dates all need a decision you have to make yourself.

  • Where it runs In your browser. The file is never uploaded.
  • Lossless Nothing is discarded. The XML holds exactly what the TOML held.
  • File size limit Up to 100 MB per file, free, without an account.

Up to 100 files at once. Mixed formats are fine.

A TOML manifest meeting an intake that has always taken XML

The interesting version of this conversion is not a config migration. It is a reporting or submission step: the facts about a project live in a TOML manifest because that is what Cargo, pip, Hugo and half the modern toolchain settled on, and the system that wants those facts — a licence register, a supplier portal, an internal compliance importer, an XSLT that produces a PDF — has read XML since 2004 and is not going to change.

That framing decides how to read the rest of this page. You are not looking for a faithful representation of TOML in XML, because nobody at the other end cares about TOML. You are looking for how close the output lands to a document shape somebody else defined, and which of the gaps you have to close by hand. There are five, and each has its own section below.

Which TOML table becomes the root element

XML needs exactly one root and a TOML document is a table with any number of keys in it, so a rule is applied. If the file has exactly one top-level key and that key holds a table, the key becomes the root element: a file containing nothing but a `[tool]` section and its sub-tables converts to a `<tool>` document. If there are several top-level keys — which is the usual case for a manifest with a `[package]` and a `[dependencies]` and a couple of loose values — everything is wrapped in a generic `<root>`.

Renaming that root is almost always the first edit, and it is a one-character-per-end change at the top and bottom of the file. If the conversion is going to be repeated, the tidier move is to reshape the TOML instead: nest the whole manifest under one table named as the schema wants it, and the converter will produce the right root every time without a post-processing step.

Arrays of tables are the part TOML and XML agree about

A `[[bin]]` block repeated three times is TOML’s way of writing a list of records, and it converts to three sibling `<bin>` elements each carrying its own children. That is exactly how XML represents a list, with no wrapper element and no index attribute, and it is the one place in this conversion where nothing has to be decided.

A plain array of scalars behaves the same way: `tags = ["a", "b"]` becomes two `<tags>` elements with text content rather than one element containing a comma-separated string. Whether the receiving schema wants that or wants a wrapping `<tags>` around `<tag>` children is the sort of thing an XSD will tell you immediately, and an XSLT transform to insert the wrapper is a dozen lines.

The four TOML date types land on three different XSD types

TOML is unusual in having four temporal types rather than one, and the conversion keeps them apart instead of collapsing them to a single UTC instant. Converting `launch = 2026-03-01T08:00:00Z`, `opens = 2026-03-01T08:00:00+02:00`, `seen = 2026-03-01T08:00:00`, `day = 2026-03-01` and `at = 08:00:00` produces, in that order, `<launch>2026-03-01T08:00:00.000Z</launch>`, `<opens>2026-03-01T08:00:00.000+02:00</opens>`, `<seen>2026-03-01T08:00:00.000</seen>`, `<day>2026-03-01</day>` and `<at>08:00:00.000</at>`. A local date stays a date and a local time stays a time.

That is the mapping to work from when you write or read the schema: the first three are lexically valid `xs:dateTime`, the fourth is `xs:date`, the fifth is `xs:time`. The one thing every value picks up is a millisecond component it did not have in the manifest — `08:00:00` becomes `08:00:00.000`. All three XSD types accept fractional seconds, so validation is unaffected, but anything comparing the text against a literal written the short way will not match. If a downstream XSLT tests `@when = '08:00:00'`, that is the line that breaks.

What the offset says, and what a local time leaves out

The offset is carried across exactly as written rather than normalised: `2026-03-01T08:00:00+02:00` arrives as `2026-03-01T08:00:00.000+02:00`, not as `06:00:00.000Z`. That is the better behaviour for a document somebody will read, and it means two elements holding the same instant can hold different text. An XSD or XPath comparison of two `xs:dateTime` values handles that correctly because the specification defines the ordering after normalisation; sorting the elements as strings, which is what an XSLT `xsl:sort` does by default, does not.

The unzoned values are the ones to think about before submitting. A TOML local datetime says the wall clock read eight in the morning somewhere, and the XML repeats that without saying where; XSD calls this timezone-unspecified and, because the zone could be anything from −14:00 to +14:00, an ordering comparison against a zoned value can come back indeterminate rather than true or false. The local time is the sharper surprise: `at = 08:00:00` has no date at all, so an element the schema declared as `xs:dateTime` rejects it and there is nothing in the source to supply the missing part. Either declare that element `xs:time`, or add the date in the TOML where it belongs.

TOML keys that are not legal XML element names

TOML lets a key be almost anything if it is quoted: `"2024 report"`, `"line-length"`, `"x:y"`. XML element names cannot start with a digit and cannot contain a space or, without a declared namespace, a colon. The writer rewrites rather than refusing: anything outside letters, digits, underscore, hyphen and full stop becomes an underscore, and a name starting with something that is not a letter or underscore gains a leading underscore.

So `"2024 report"` becomes `<_2024_report>`, `"a b"` becomes `<a_b>` and `"x:y"` becomes `<x_y>`. That last one is the one to watch: a key written with a colon because somebody meant a namespace prefix loses the colon and becomes an ordinary name, which is not what the schema is expecting. Hyphens and full stops survive untouched, so `line-length` and `tool.black`-style names inside a single quoted key are fine.

Producing XML attributes from a TOML table

By default every TOML key becomes a child element, and most schemas want at least some values as attributes instead. The convention the writer follows is a leading at sign on the key name: inside a table, `"@id" = 42` produces `id="42"` on that element rather than an `<id>` child. TOML allows the key because it is quoted.

Element text alongside attributes uses the key `"#text"` in the same way. Both are worth knowing about because they turn a two-pass job into one — reshaping the TOML with a handful of at-signs is quicker and more repeatable than converting and then moving values into attributes with an XSLT. Attribute values are escaped for ampersands, angle brackets and double quotation marks, so a version string or a URL is safe to place there.

What the XML document does not carry from the TOML

No XML declaration is written, so the document begins at the root element. No namespace is declared, no schema location is referenced, and no processing instruction appears. The output is indented two spaces per level and ends with a newline. Text and attribute values are escaped for `&`, `<`, `>` and `"`.

An empty TOML array is the other quiet one: `tags = []` produces no element at all, only a blank line where one would have gone. That is defensible — there is no member to repeat — and it will still surprise a schema that declares the element with `minOccurs="1"`. Between that, the generic root and the element names that had to be rewritten, the honest summary is that this conversion gets you most of the way to a document and never all the way to a valid one.

The comments are why the TOML stays the source of truth

Both formats support comments and neither the reader nor the writer moves them across, so every `#` line explaining why a dependency is pinned or why a setting has an unusual value is absent from the XML. That is not a small loss in a manifest, where the comments are frequently the only record of a decision.

Treat the XML as a generated artefact, not as a copy. Regenerate it whenever the manifest changes rather than editing it and hoping the two stay in step, and keep it out of the repository unless the submission process requires a checked-in copy — in which case a generation step in the build is far safer than a file somebody remembers to update.

Validate the XML against the schema before submitting it

Everything above is a reason the first attempt will not validate: the root will have the wrong name, a local time will not satisfy an element declared `xs:dateTime`, some values will be children where the schema wants attributes, and an optional list may be missing entirely. Finding all of that in one run is much faster than finding it in four rejection emails.

`xmllint --schema contract.xsd out.xml --noout` gives you the complete list of complaints with line numbers, and is available on nearly every machine. Work through them by reshaping the TOML rather than by editing the XML, so the next run starts from a better place. If the schema is genuinely complex — nested namespaces, substitution groups, mixed content — this conversion is the input to an XSLT rather than the finished document, and treating it that way from the start saves an argument later.

Where a TOML file is converted, and how large it can be

In this browser tab, by plain JavaScript. No engine is downloaded, nothing is uploaded, there is no account and no daily allowance, and the network tab during a conversion is how to verify that rather than this sentence.

The free limit is 100 MB, which no manifest will ever approach — these files are measured in kilobytes. The size that does change is the output: XML repeats every name twice, once opening and once closing, and adds indentation, so expect the document to be two to three times the manifest it came from. That is the ordinary cost of the format and it compresses away over any transport that gzips.

How to turn a TOML manifest into an XML document

  1. Note which TOML dates carry an offset and which are local, before you start.
  2. Drop the .toml file onto this page; tables become elements in your browser.
  3. Rename the root, then validate the .xml against the schema before you send it.

TOML and XML: typed tables against an element tree

TOML compared with XML
TOMLXML
Full nameTom's Obvious Minimal LanguageExtensible Markup Language
File extension.toml.xml
Media typeapplication/tomlapplication/xml
First published20131998
Published byW3C
SpecificationTOML 1.0XML 1.0
LicensingOpen standardOpen standard
Standing todayCurrentCurrent
Opens in a browserNo browserEvery browser
Considered insteadYAML, JSON, INIJSON, YAML

What survives

Nothing is discarded. TOML and XML both store their content losslessly, so the conversion is a change of packaging rather than a change of quality, and it can be repeated without accumulating damage.

Comments carry across. Both TOML and XML have a comment syntax, so notes left for whoever maintains the file next are not silently thrown away.

Opening the result

XML opens in every current browser. TOML has narrower browser support than that. If the file is going onto a web page or into a form, that is usually the whole reason for the conversion.

Visual Studio Code reads both TOML and XML, so there is a way to check the result against the original without a second tool.

What each format is for

The two are aimed at different work: TOML at editing, XML at moving data between programs. That is worth weighing before converting, because the reason one exists is usually the reason the other is awkward.

TOML was published in 2013. The specification is TOML 1.0, and it is worth reading if the file has to outlive the tool that wrote it.

XML comes from W3C and dates from 1998, specified as XML 1.0. Visual Studio Code and oXygen XML Editor all read it.

XML was published in 1998 and TOML in 2013. The older one is generally the safer file to hand to somebody; the newer one usually does the job in fewer bytes.

TOML to XML: roots, attributes and dates

Are my TOML files uploaded anywhere?

No. This conversion runs entirely inside your browser, so the file never leaves your device. You can confirm it yourself: open the network tab of your browser's developer tools and convert something. You will see the page load, plus the analytics and advertising the site is paid for with — and nothing carrying your file.

What will the root element be called?

If the TOML has exactly one top-level key and it holds a table, that key becomes the root — a file containing only a [tool] section gives you . Anything else, including several top-level keys, is wrapped in a generic that you will want to rename.

Which XSD type does a TOML date arrive as?

Whichever one matches the TOML type it was. An offset datetime and a local datetime are both valid xs:dateTime — 2026-03-01T08:00:00.000Z and 2026-03-01T08:00:00.000. A local date 2026-03-01 is an xs:date. A local time 08:00:00.000 is an xs:time and will not validate against an element declared xs:dateTime.

How do I get attributes rather than child elements?

Prefix the key with an at sign, which TOML allows if you quote it: "@id" = 42 inside a table becomes id="42" on that element. Everything without the prefix becomes a child element.

Does the output have an XML declaration?

No, and no namespace declarations either. The document starts at the root element. Most parsers accept that and infer UTF-8; anything that insists on a declaration needs one line pasted at the top.

What happens to an array of tables?

It becomes the same element repeated, which is exactly how XML holds a list. Two [[bin]] blocks give you two elements as siblings, each with its own children.

Are the comments carried over?

No. Both formats support comments and neither the reader nor the writer moves them, so every explanatory line in the TOML is gone from the XML. That is the strongest argument for keeping the TOML as the file people edit.

More about these formats