HTML Table to XML Converter

Turn a published table into a message. Merged cells are flattened first, you name the elements, and a header like Q1 (actual) does not produce a file that refuses to parse.

To convert an HTML table to XML, paste the markup above. Every table on the page is listed; the one you choose becomes a root element containing one element per row, both names configurable. Header cells that are not legal XML element names are rewritten, with the original spelling kept in a name attribute so the document parses and nothing is lost.

Need to rename the columns first? Open the app

Two formats from the same family that agree on almost nothing

HTML and XML look alike and are not. HTML forgives an unclosed tag, an unescaped ampersand and an attribute with no quotes. XML forgives nothing, and the entire value of converting from one to the other is that the result actually parses.

The bigger gap is naming. HTML has no rules about what a header cell can say, so published tables have headers like Q1 (actual), % change and 2024 Target. None of those is a legal XML element name, and writing one produces a document the recipient cannot open.

Worked example

A published table with headers written for people:

<table>
  <tr><th>Region</th><th>Q1 (actual)</th><th>% change</th><th>2024 Target</th></tr>
  <tr><td>North Coast</td><td>1840</td><td>9.4</td><td>4000</td></tr>
</table>

With the root named regions and the row element named region:

<?xml version="1.0" encoding="UTF-8"?>
<regions>
  <region>
    <Region>North Coast</Region>
    <Q1__actual_ name="Q1 (actual)">1840</Q1__actual_>
    <__change name="% change">9.4</__change>
    <_2024_Target name="2024 Target">4000</_2024_Target>
  </region>
</regions>

Three of the four headers were illegal and all three were corrected deterministically, so the same page always produces the same element names. Each one keeps its original spelling in a name attribute, which means a person reading the document can still see the header the page used and a stylesheet written against it has something to match on.

Spans, and why they matter more here

A rowspan that is ignored produces a row with fewer cells than the header. In CSV that shows up as a short row and gets padded with a warning. In XML it produces a row element with fewer children than its siblings, which is a document whose shape varies, and that is exactly what a schema validator rejects.

So the rectangle is built first: colspan repeats the value across the columns it covers, rowspan fills it down, and every row element ends up with the same children in the same order. An empty cell becomes an empty element rather than a missing one, for the same reason.

Elements, attributes, and what is not generated

  • Child elements are the default: readable, no length limit, and safe for a value containing anything once it is escaped.
  • Attributes give you one self-closing element per row, which is much smaller and matches a lot of older schemas. An attribute cannot usefully carry a line break, so a table with wrapped cells wants elements.
  • No schema, no namespace. Both belong to the specification you were given, and a generated one would look official while disagreeing with it.
  • No nesting from a dot in a header. A header called q1.actual becomes an element spelled that way, because inferring structure from punctuation breaks every header that legitimately contains a dot.
  • Markup inside a cell is reduced to its text, so a bold figure is a figure and a link is its label.

Escaping, in the direction people forget

Coming from HTML, most of the entities are already entities: the page said &amp; and meant an ampersand. The parser resolves those back to characters, and then the XML writer escapes them again. That round trip is why an ampersand in a company name survives instead of becoming a broken entity or a doubled one.

Characters that were raw in the source, which HTML tolerates and XML does not, are escaped on the way out. This is the specific reason a hand-written HTML table cannot simply be renamed to .xml and sent.

Named entities that exist in HTML and not in XML are the other half of the problem. XML defines exactly five: &amp;, &lt;, &gt;, &quot; and &apos;. HTML defines well over a thousand, so a page using &nbsp; or &pound; produces a document an XML parser rejects at the first one it meets. Because the markup is parsed properly first, those arrive here as characters and leave as characters, and the problem never reaches the recipient.

Which table, and the ones that are not tables

Every table on the page is found, listed with its caption and its row and column counts, and the first converts on arrival. Taking whichever appears first in the markup is the default behaviour of most converters, and on a published report that usually means a summary box rather than the detail you came for.

A table nested inside another table's cell is treated as layout rather than data. That construction is almost always a page built before CSS layout was reliable, and lifting it out as a separate result produces two documents where one was wanted. Its text stays in the cell that holds it and a note tells you how many were skipped, so nothing disappears without a word.

Frequently Asked Questions

My headers have spaces and brackets. Will the XML parse?

Yes. Illegal characters become underscores and a name that cannot start where it starts gains a leading one, so Q1 (actual) becomes Q1__actual_ and 2024 Target becomes _2024_Target. Each element carries the original in a name attribute and a note says how many were rewritten.

Why not just rename the .html file to .xml?

Because HTML tolerates unclosed tags, raw ampersands and unquoted attributes, and XML tolerates none of them. Nearly every real HTML table fails as XML for at least one of those reasons, before you even get to the element names.

How are merged cells handled?

Resolved into a rectangle before anything is written, so every row element has the same children. Left unresolved they produce row elements with different numbers of children, which is a document whose shape varies and the first thing a validator rejects.

What does an empty cell become?

An empty self-closing element. Omitting it would make the structure vary from row to row, which defeats the point of producing XML for a system that validates.

Can I get attributes instead of child elements?

Yes, one switch. Attribute mode is far more compact and matches many older schemas. It cannot carry a line break in a value, so if the table has wrapped cells in it the element mode is the one you want.

Does it generate a DTD or XSD?

No. A schema belongs to the specification you were sent, and an invented one would look authoritative while disagreeing with it. What is guaranteed here is a well-formed document with the element names you chose.

Turn the page table into a message

Spans resolved, names made legal, originals preserved, well formed on the first attempt.

Back to the converter