A worksheet is a grid. XML is a tree.
Every conversion from Excel to XML answers the same question: which part of the grid becomes the parent, and which part becomes the child. A spreadsheet has no answer built in. Rows and columns sit at the same level of importance, and the meaning lives in a header row a human reads. Turning the sheet into XML forces you to name three things the file never held, and the quality of the output comes down to those three names plus how the cell types are handled on the way through.
sku | product name | unit price
KB-88 | Mechanical Keyboard | 189
MS-12 | Wireless Mouse | 30.25<data><row><sku>KB-88</sku><product_name>Mechanical Keyboard</product_name><unit_price>189</unit_price></row></data>The three names you pick
Two of them sit in the controls above. The third comes out of your header row, one element name per column.
| Name | Where it lands | What to write there |
|---|---|---|
| Root | The single outer element wrapping the whole document | What the file is a list of, in plural form: products, invoices, employees. XML allows exactly one root, so this element appears once. |
| Row | One element per data row | The singular of the root: product, invoice, employee. Anything reading the file will loop over these. |
| Column | One element or attribute per cell | Read from your header row, then rewritten where the heading breaks XML naming rules. |
The defaults of data and row load the file and tell a reader nothing. If the XML is going into a system with a schema waiting for it, match the names in the schema before you convert. Renaming an element across ten thousand rows afterwards is a find and replace nobody enjoys running twice.
Column headings XML will not accept
An XML element name has rules a spreadsheet heading never had to follow. It starts with a letter or an underscore, holds no spaces, and reserves anything beginning with the letters xml. Real headers break all three constantly, so each one is rewritten and the note panel lists what changed.
| Heading in the sheet | Element name written | Reason |
|---|---|---|
product name | product_name | Spaces end a name in XML, so the parser would read a second attribute |
Q1 % | Q1__ | Percent, slash, comma, brackets and currency symbols are all outside the allowed set |
2026 total | n_2026_total | No XML name starts with a digit, so a prefix goes on the front |
xmlVersion | x_xmlVersion | Names starting with xml in any casing are reserved by the specification |
| Blank cell in the header row | column4 | An unnamed column still needs a tag, numbered by position |
Two columns both called total | total, total_2 | Sibling elements repeat legally, but a repeated name under one row is ambiguous to read back |
Rewritten names break the round trip. If the XML is going back into a spreadsheet later, product_name comes home as a column called product_name, not product name. Rename the headers in Excel before converting when the exact wording matters, since a heading you control is easier to fix than a mapping file you have to maintain.
What each kind of cell turns into
XML holds text and nothing else. Every cell type in the workbook has to be flattened into a string, and a few of those flattenings lose information worth knowing about.
| Cell in Excel | Text in the XML | What to watch |
|---|---|---|
| Text | The string as stored | Ampersands and angle brackets are escaped to & and <. Control characters XML 1.0 forbids are stripped. |
| Number | The stored value, not the displayed one | A cell showing $1,200.00 writes as 1200. Display formatting lives in the sheet, not the data. |
| Date or time | 2026-01-14 or 2026-01-14T09:30:00 | Excel stores dates as serial numbers counted from 1900. The ISO option converts them. Turn it off and you get full timestamps instead. |
| Boolean | true or false | Lowercase, matching what XML Schema and most parsers expect from a boolean. |
| Formula | The last calculated result | The formula text is dropped. A workbook saved before recalculation carries stale values into the XML. |
| Blank | <price/> or an empty attribute | An empty element and a missing element mean different things downstream. Every column is written for every row, so a gap in the sheet becomes an empty tag rather than an absent one. |
| Error such as #N/A | The error text | Clean these out in Excel first. An #REF! string sitting in a price field passes schema validation on type string and fails everything after. |
Child elements or attributes
Both are valid XML for the same data, and the choice is the one people argue over longest. Element mode nests each cell in its own tag. Attribute mode puts every value on the row tag and closes it in one line.
<row><sku>KB-88</sku><unit_price>189</unit_price></row><row sku="KB-88"
unit_price="189"/>Attribute output runs roughly forty percent smaller on a wide sheet, which matters when the file crosses a network. It also closes doors. An attribute appears once per element, holds no child structure, and normalises whitespace, so a multi line address or a value with tabs in it comes back changed. Pick attributes for compact identifiers and flags. Pick elements for anything a person typed, anything that might grow children later, and anything a schema already describes as an element.
Workbooks with more than one sheet
Loading a file shows a tab per worksheet, and the active sheet converts on its own by default. Switch to whole workbook and each sheet becomes a named wrapper element under the root, keeping the sheet name as structure rather than losing it.
<data><Orders><row>...</row></Orders><Customers><row>...</row></Customers></data>Worth knowing before you reach for it: each sheet gets its own header row and its own set of column names, so a workbook where the sheets hold unrelated shapes produces XML with unrelated shapes inside one document. That is fine for an archive and awkward for a schema. When the sheets are monthly slices of the same table, convert them one at a time and concatenate the row elements instead.
Five things to check before the file goes anywhere
- Merged cells. A merge stores the value in the top left cell and leaves the rest of the range empty. A title merged across five columns writes one populated element and four empty ones. Unmerge and fill down in Excel first, and the note panel flags the ranges it found.
- Leading zeros. A SKU of 00457 stored as a number arrives as
457. Excel dropped the zeros long before the conversion started. Format the column as text in the sheet, then reload the file. - Long numbers. Anything past fifteen digits, which covers most credit card numbers and some barcodes, loses precision inside Excel itself. Check a sample against the source system rather than against the sheet.
- Trailing blank rows. A sheet where someone deleted content without deleting rows reports a used range far past the data. Dropping blank rows is on by default for exactly this reason, and the note panel counts what went.
- Hidden rows and filters. Hidden rows are still rows. Everything in the used range converts, filtered out or not. Delete what you do not want rather than hiding it.
Working through a real export
- Save the workbook from Excel with formulas recalculated, so the cached values in the file match what the sheet displays.
- Drop the file above and pick the sheet holding the table. The preview shows the first eleven rows so you catch a title row sitting above the headers.
- Set the root and row names to whatever the receiving system expects. If a schema exists, take the names from the schema.
- Read the note panel. Renamed headings and merged ranges are the two entries worth acting on before you go further.
- Download the file and validate it against the target schema. Structure is correct by construction here. Whether the values satisfy the schema is a separate question this tool has no way to answer.
What this converter leaves to you
- No XSD or DTD. The output is well formed XML with no schema attached. Generate or hand write the contract separately.
- No nesting past sheet, row and cell. A column holding comma separated values stays one flat element. Splitting
tagsinto repeated<tag>children needs a rule only you know. - No namespaces. Nothing here writes a prefix or an
xmlnsdeclaration. Add them after conversion if the target needs them. - No formula recalculation. Cached results are read from the file. Nothing is evaluated.
- No cell formatting, colours, comments, charts or images. Only values cross over. Everything visual stays in the workbook.
- Large files slow the tab down. Parsing and generation run on the main thread in your browser. Sheets in the tens of thousands of rows convert fine. A file past a few hundred thousand rows belongs in a script rather than a page.
Nothing you load is uploaded. The workbook is parsed in this page and the XML is built in this page, so a sheet holding payroll or customer records never leaves your machine. Load the page once, drop your network connection, and the converter keeps working. Going the other direction is covered by the XML to Excel converter, and a plain comma separated file with no workbook around it is a better fit for CSV to XML.
