Skip to main content

CSV to XML: Map Every Row to a Record Element the Right Way

A practical guide to converting CSV to XML — each row a record element, columns as child tags or attributes, custom root and row names, and safe escaping for legacy and SOAP imports.

Published By Li Lei
#csv to xml #xml #data conversion #soap #developer tools

CSV to XML: Map Every Row to a Record Element the Right Way

Most data lives in spreadsheets, but a surprising amount of software still refuses to read one. SOAP services, older ERP importers, Android resource files, and plenty of enterprise schemas only accept XML. So you end up with a CSV export in one hand and an upload form that rejects it in the other. The fix is a clean, predictable conversion: turn the table into XML where the document has a single root, every data row becomes a repeating record element, and each column becomes a child tag (or an attribute, when the target schema asks for that).

This post walks through how that mapping actually works, where escaping matters, and how to name your elements so the output drops into a real importer without hand-patching. You can follow along in the CSV to XML Converter, which does the whole thing in your browser.

The core mapping: one root, one element per row

XML has a rule CSV does not: a document must have exactly one root element. A CSV is a flat list of rows with no outer container, so the first job of any converter is to wrap the whole table in a single root. By default that root is rows, but you set it to whatever your target expects — Transactions, catalog, Employees.

Inside the root, the conversion is mechanical and worth stating plainly:

  • Each data row becomes one record element. The default name is row, and you rename it to the singular noun your schema uses (Transaction, product, string).
  • Each column becomes a child tag of that record element. The tag name comes from the header cell, so the column name becomes <name> and the column price becomes <price>.
  • The cell value becomes the text inside that tag.

That third bullet is the part people forget to plan for, and it's where escaping comes in. Once the model is "header names the tag, cell fills the text," the output is deterministic and you can reason about it.

A worked example

Take this four-line CSV with a header row:

id,name,note
1,Widget,"In stock, ships today"
2,Bolt & Nut,"<promo> 10% off"

With the root set to products, the row element set to product, and columns kept as child elements, the converter produces:

<?xml version="1.0" encoding="UTF-8"?>
<products>
  <product>
    <id>1</id>
    <name>Widget</name>
    <note>In stock, ships today</note>
  </product>
  <product>
    <id>2</id>
    <name>Bolt &amp; Nut</name>
    <note>&lt;promo&gt; 10% off</note>
  </product>
</products>

Two things in that output are doing quiet, important work. The note cell "In stock, ships today" contains a comma, but because it was wrapped in double quotes in the source, an RFC 4180 parser keeps it as one field instead of splitting it into two columns. And the & in Bolt & Nut came out as &amp;, while <promo> became &lt;promo&gt;. Without that escaping, a raw < would open a phantom tag and the whole document would fail to parse.

Elements or attributes: pick by schema, not habit

The converter lets you emit columns as child elements (above) or as attributes:

<product id="1" name="Widget" note="In stock, ships today"/>

Attributes are more compact and they diff cleanly, which is why many legacy importers and config formats prefer them. But the trade-offs are real. Attributes are meant for short scalar values: they hold newlines badly, and strict XML gives no order guarantee, so a multiline description field reads poorly as an attribute. My rule of thumb is to reach for attributes only when the records are flat and the values are short — a product feed of sku, price, stock is a good fit; a table with a free-text comments column is not.

Either way, escaping adapts. In attribute mode the converter also escapes " and ' so a title like 9" Pan & Pot stays valid inside the quotes, not just &, <, and >.

Naming, headers, and the legal-tag problem

A header like Order ID is a perfectly fine spreadsheet column name and a completely illegal XML tag name — tags can't contain spaces or colons, and they can't start with a digit. A good converter doesn't choke on this; it sanitizes. Spaces and colons become underscores, so Order ID turns into Order_ID. A header that starts with a digit, like 2024, gets a leading underscore (_2024), and so does anything starting with xml, which the spec reserves.

The same sweep handles the messy cases. A blank header cell falls back to col1, col2, and so on. Duplicate headers get a numeric suffix, so two columns both called id become id and id_2. And if you uncheck "first row is header," every line is treated as data and columns are auto-named col1, col2, ... from the widest row. The upshot: the output always parses, even from a sloppy export. The one thing to watch is that the tag names may not match your schema verbatim — if the importer demands exact names, rename the headers in the source first, then convert.

Why this matters for SOAP and legacy imports

When I first wired up a CSV-to-XML step for a finance feed, the failures were never about the data itself. They were about a single unescaped ampersand in a memo field, or a delimiter mismatch that crammed an entire row into one column because the file was semicolon-separated (common in European exports, where the comma is the decimal mark). The actual numbers were fine; the markup around them was broken. That's the whole reason a purpose-built converter beats a quick regex: it owns the boring, brittle parts — RFC 4180 quoting, escaping, legal tag names, delimiter selection — so the output is valid for a SOAP envelope or an enterprise importer on the first try.

A few practical settings make the difference here. Set the delimiter to match your source (semicolon, tab, or pipe, not just comma). Toggle the <?xml version="1.0" encoding="UTF-8"?> declaration on if the consumer expects a prolog, off if it doesn't. And choose 2 or 4-space indentation to match the surrounding file — 4-space is handy when you're slotting generated <string> records into an Android strings.xml.

After conversion: format and round-trip

Once you have XML, two follow-ups come up often. If the output is going into a file a human will read or diff, run it through the XML Formatter to pretty-print and validate the structure before you commit it. And if you later need to pull the data back out — say the downstream system hands you XML and you want a table again — the reverse trip is its own job; keeping the root and row names consistent on the way out makes that round-trip far less painful.

The honest summary: CSV-to-XML is simple in concept and full of small traps in practice. Get the mapping right (one root, one element per row, columns to tags), let the tool handle quoting and escaping, and name your elements to match the target schema. Do that and the XML you paste into a SOAP body or an importer just works, instead of failing on a stray & three rows down.


Made by Toolora · Updated 2026-06-13