CSV to XML
This converter turns CSV rows into XML elements in your browser: a <records> root, one <record> element per row, one child tag per header. Tag names are sanitised to legal XML, text is escaped, and dotted headers nest into child elements. Nothing is uploaded.
La interfaz de esta herramienta está en inglés.
La guía de abajo solo está disponible en inglés.
CSV to XML explained
XML is stricter than CSV about names, and that strictness is the whole engineering problem here. A header like unit price cannot become a tag because element names may not contain spaces; 2fa cannot become a tag because names may not start with a digit. This converter applies the standard remedy — invalid characters become underscores, and a name that would start with a digit gets a leading underscore — so every header produces a tag a real XML parser accepts, and the mapping from column to element stays visible rather than magical.
The CSV side is parsed to RFC 4180: a state machine that honours doubled quotes, commas and newlines inside quoted fields, delimiter sniffing across comma, semicolon, tab and pipe, and the same narrow type policy the site uses everywhere — values become numbers only under the JSON number grammar, so 02134 keeps its leading zero. Dotted headers keep their structure: address.city becomes an address element with a city child, not a flat tag with a dot baked into its name.
Everything runs in this browser tab. The CSV is parsed by an in-page engine and the XML is built from the parsed records; nothing touches a network — open DevTools, watch the Network panel stay silent, and the privacy claim checks itself.
Checking the round trip is one paste away, because the XML to CSV converter reads these same conventions in reverse, rows back out of elements.
If the choice of output format is still open, the JSON vs XML comparison lays out where markup earns its keep and where it is overhead.
How the conversion actually works
The pipeline is parse, expand, sanitise, escape — in that order, and each stage only sees the output of the one before. That ordering is why a comma inside a quoted cell never disturbs the element structure: the grid is settled before any XML exists, and the XML writer never makes decisions about CSV syntax.
- Parse: RFC 4180 rules with the real-world allowances — a quote in the middle of a bare field is taken literally, and empty trailing rows are dropped.
- Expand: headers are decoded as paths, so address.city nests city inside address, and tags[0], tags[1] become repeated elements.
- Sanitise: every element name is checked against the XML 1.0 name rules — letters, digits, underscores, hyphens, dots and colons — and repaired where it fails.
- Escape: text content gets &, < and > where the source had &, < or >, and attribute values additionally escape the double quote.
The tag-name policy, precisely
XML 1.0 lets a name start with a letter, an underscore or a colon, and lets it continue with digits, hyphens and dots; everything else is out. The sanitiser replaces every out-of-set character with an underscore and prefixes an underscore when the first surviving character would be a digit — so unit price becomes unit_price, 2fa becomes _2fa, and a column named discount% becomes discount_. Valid names pass through untouched, including CamelCase and kebab-case, because rewriting a name that was already legal would break consumers waiting for it.
| CSV header | Element emitted | Why |
|---|---|---|
| name | <name> | already a legal XML name — passed through |
| unit price | <unit_price> | space replaced with an underscore |
| 2fa | <_2fa> | names cannot start with a digit |
| address.city | <address><city> | dotted headers nest, not flatten |
What happens to values
XML has no separate number or boolean type — every value is text — so cells cross over as their exact characters, with no coercion to lose or invent precision. An empty cell is treated as an absent field rather than an empty value, so the element is omitted instead of emitting a string of self-closed tags nobody consumes. Escaping is applied on the way out: a cell containing A & B becomes A & B in the document, and any XML parser reading the result sees the original text again.
- 02134 stays 02134; no leading zero is ever trimmed, because the value is text from the moment it leaves the grid.
- A multi-line cell keeps its line breaks inside the element, and those breaks are data, not formatting.
- The <record> element is the same for every row, ragged or not — columns a row lacks simply produce no element.
Edge cases and how they are reported
The converter's refusals are the load-bearing part of its contract. An unterminated quoted field stops the parse at that line with a named error, because guessing where the field ended would silently reshape every row after it. A row with a different field count than the header still converts, but a warning states that missing fields are absent and extras were dropped. Duplicate headers convert with the rightmost value winning, and the warning says so — the spreadsheet-import behaviour, made explicit.
- Unterminated quote: parse aborted, line reported, no output written.
- Ragged rows: converted with a warning naming the affected rows.
- Duplicate headers: converted with a warning; rightmost occurrence wins.
Frequently asked questions
Does converting CSV to XML upload my file anywhere?
No. The CSV parse and the XML build both run inside this browser tab with code that was loaded once at page open. There is no upload endpoint to call, and the page is served with a Content-Security-Policy whose connect-src is 'self', so the browser would refuse any outbound request even if one were attempted. Watch the Network panel in DevTools while converting — it stays empty.
Why is my output wrapped in <records> and <record> tags?
XML needs exactly one root element, and a CSV file has no natural name to give it, so the converter uses a fixed shape: a <records> root with one <record> element per row. Headers become the child tags inside each record. The wrapper is what makes the output a well-formed document rather than a fragment, and every row is easy to address with an XPath like /records/record.
How are illegal tag names handled?
Every header is sanitised to the XML 1.0 name rules: characters outside letters, digits, underscore, hyphen, dot and colon become underscores, and a name that would start with a digit gets a leading underscore. So unit price becomes unit_price and 2fa becomes _2fa. Names that were already legal pass through untouched, which matters when a downstream system matches on exact tag names.
How are special characters in cell values escaped?
Text content escapes the three characters XML requires: & becomes &, < becomes < and > becomes >. Inside attribute values the double quote is escaped as " as well. Escaping happens on output only, so a cell holding A & B round-trips back to exactly A & B when an XML parser reads the document — nothing is escaped twice and nothing is pre-unescaped.
Will my numbers and zip codes keep their formatting?
Yes, because values become XML text verbatim — there is no numeric coercion step that could reformat them. 02134 keeps its leading zero, 44.95 keeps its exact decimal form, and a value like 1e5 stays the characters you typed rather than being expanded. Type information is a CSV-or-JSON concern; XML elements carry text, and the converter does not second-guess it.
What if my CSV has ragged rows or duplicate headers?
Both convert, each with an explicit warning. A row shorter than the header is completed with absent fields — no empty elements are invented — and a row longer than the header has its extras dropped, with the warning naming how many rows were affected. Duplicate column names resolve rightmost-wins, the same way a spreadsheet import behaves, and the warning lists the names involved.
Which related tools should I use next?
- XML to CSVConverter — runs in your browserOpen
- CSV RFC 4180 RulesPlain-English guideOpen
- JSON vs XMLHead-to-head comparisonOpen
- CSV to JSONParse a spreadsheet export into records, quoting and all.Open
- JSON to XMLConvert with attributes, and names XML will accept.Open
- JSON ValidatorValidate syntax with exact line and column, and repair it in one click.Open