Convert CSV to XML
Your header row names the elements. Headers XML will not accept are rewritten by a rule you can read, on screen, before you download.
- SECURE
- NO UPLOADS
- NO SIGNUP
- BROWSER BASED
- FREE
- FOREVER.
Support us with a link or a share
- Rows
- —
- Columns
- —
- Separator
- —
- Encoding
- already text
Paste rows or drop a .csv file — the element names appear before anything is converted.
<rows><row>…</row></rows> — names follow the same rule as the headers.
A CSV column can be called anything. An XML element cannot.
Every other part of this conversion is mechanical. A row becomes an element, a cell becomes the text inside a child element, and the file is done. The one place a converter can be good or bad is the header row, because that is where two formats disagree about what a name is allowed to be.
A spreadsheet column can be called First Name, 2026 sales,
Unit price ($) or nothing at all. An XML element name follows a grammar: no
spaces, no parentheses, no currency signs, and it may not begin with a digit, a hyphen or a
full stop. Hand <First Name> to any parser and it stops on the first row.
So a header that XML will not accept has to be rewritten — the only question is whether you
get told how.
The rewriting rule, in full
Five steps, applied in this order, and every one of them is visible in the element list under the tool before you download anything:
- Whitespace around the header is trimmed.
-
Every character XML does not allow inside a name becomes one underscore —
one per character rather than one per run, so
a bgivesa__band you can still tell how many characters were replaced. -
If the name still starts with something a name cannot start with — a digit, a hyphen, a full
stop — it gains a leading underscore.
2026 salesbecomes_2026_sales. -
Names beginning with the letters
xml, in any mix of case, are reserved by the specification, so those gain a leading underscore too. -
A column with no header at all is named after its position:
column_3. It is never dropped, and no row of your data is ever consumed to invent a name for it.
A colon is the interesting case. It is technically legal inside an XML name, which is why some
converters leave it alone, but XML Namespaces reserves it to mean “everything before this is a
declared prefix”. A header called order:id means a column with a colon in its
name; it does not mean a namespace called order, and a namespace-aware parser
will reject the document for referring to a prefix nothing declared. So the colon is rewritten
here, and the reason is written on the line next to it.
Why your ampersand comes back doubled, and why that is correct
Three characters are markup in XML: &, < and
>. Inside an attribute a double quote joins them. Each one is written as an
entity, which is ordinary and uninteresting until a cell already contains something like
AT&T.
That cell holds five characters: an ampersand, then a, m,
p and a semicolon. Write them raw and an XML parser resolves the entity and hands
the recipient a single ampersand — five characters in, one character out, silently. Escaping
the ampersand again produces &amp;, which a parser turns back into
exactly the five characters your file held. It looks like a bug in a preview. It is the
difference between a converter that preserves your data and one that quietly rewrites it, and
it is the reason this page reports how many cells in each column were escaped and shows one of
them before and after.
Two smaller cases follow the same logic. A carriage return is written as
, because an XML parser normalizes a literal carriage return to a line
feed before your program ever sees it. And in attribute mode line breaks and tabs are written
as numeric references as well, because attribute values are normalized harder still — every
literal one of them arrives as a plain space.
Child elements or attributes, and the rule that actually differs
Child elements are the default. They read better, they hold line breaks without a fight, and most things that consume XML expect them. Attributes make a much smaller file and are what some older importers want.
There is one real difference beyond taste. XML allows two sibling elements to share a name —
<row><id>1</id><id>2</id></row> is perfectly
well-formed and means what it looks like. It forbids two attributes on one element sharing a
name, and that is a fatal error rather than a matter of style. So a file with two columns
called id converts cleanly as elements and needs one of them renamed as
attributes. This tool renames it only in the mode that requires it, and tells you which
column it renamed. That is the opposite of what happens on the
CSV to JSON converter, where an object cannot hold a duplicate key
at all and the second one always has to be numbered.
Characters XML cannot carry at any price
Most of the ASCII control characters are excluded by XML 1.0 outright. Not “discouraged” —
there is no valid XML document containing a U+0007, and you cannot smuggle one in
as  either, because the numeric reference is exactly as invalid as the
character. They turn up in real exports more often than you would think, usually from a
database field that once held a terminal escape sequence.
Nothing here refuses a file over that, or over its size. The characters that cannot exist in the format are removed, the rest of the cell is kept exactly as it stands, and the column, the count and the code point of the first one are printed. A limit that comes from the format is worth stating; a limit invented by the converter is not.
What this does not do, and what to use instead
There is no template mode with ##1## placeholders. FreeFormatter needed one
because it did not read your header row at all — the template was how you told it what the
columns were called. Here the header row does that job, so a template would be a second way to
say the same thing, and a setting that duplicates a default is a setting nobody can predict.
There is also no XML to CSV converter here, and that is a judgement rather than an oversight. This direction is always honest: every grid can be written as a list of elements. The return trip is not, because a CSV is a grid and an XML document is a tree, and an arbitrary tree — nested three deep, with repeated children and attributes on the branches — has no obvious grid form. A converter that pretends otherwise picks one for you. If what you have is an XML document you need to read rather than reshape, the XML formatter indents it and shows you its structure. And when the XML that comes out of here has to become a different shape of XML — or HTML, or plain text — that is what a stylesheet is for: the XSLT transformer runs one against it in the same tab, with nothing uploaded either.
Many people arrive at this problem from Excel, which can indeed export XML — but only after
you attach an XML Map to the workbook, which means writing a schema first. That is the right
route when the receiving system dictates a schema. It is a great deal of work when what you
actually wanted was your rows in angle brackets. If the source is a workbook rather than a
.csv, CSV to Excel goes the other way for the same
family of files. Why a separator picker and an encoding picker are needed under the tool at
all is its own story, told in
this explanation of the CSV format: there is no standard to
appeal to, only conventions that disagree.
Where the CSV to XML conversion actually runs
Inside this tab, start to finish. Decoding the bytes, naming the elements and assembling the document happen in JavaScript that arrived with the page, and nothing on this domain will take a spreadsheet even if you aim one at it. Past roughly half a megabyte the job moves onto a background thread and the document is assembled in slices, which is why the bar you see counts rows already written rather than time already spent — a bar that filled at a steady pace regardless of the file would be decoration pretending to be information. Two of the four converters ranking above this page for the same search post your rows to a server, and one of them says as much in its opening sentence. Given that a spreadsheet headed for XML is typically an order feed, a product catalog or a staff list, that difference is worth more than any feature comparison, and your network panel will settle it in about four seconds.
Separator detection, quoting and encoding are shared with the rest of the CSV family here, so a semicolon file from a European Excel and a UTF-16 export from a database both read the same way they do on the CSV viewer and on CSV to SQL. What is different on this page is only what comes out the other end.
Questions
What happens to a column called "First Name"?
It becomes <First_Name>, and the page tells you so before you download anything. An XML element name cannot contain a space — that is the format’s rule, not a preference — so every character XML will not accept becomes exactly one underscore. One per character rather than one per run, so that "a b" gives a__b and you can still tell how many characters were replaced. A header that starts with a digit gets a leading underscore, because a name cannot start with one either, and a column with no header at all is named after its position: column_3.
Why does my cell that said & come out as &amp;?
Because that is the only answer that gives your data back unchanged. If the cell literally holds the five characters & a m p ;, then writing them raw means an XML parser hands the recipient a single & — five characters in, one character out. Escaping the ampersand again produces &amp;, which a parser turns back into exactly the five characters that were in your file. It looks wrong in a preview and it is the difference between a converter that preserves your data and one that quietly rewrites it.
Should the values be child elements or attributes?
Child elements unless something at the other end wants attributes. They read better, they hold line breaks without a fight, and — the real difference — XML allows two sibling elements with the same name but forbids two attributes with the same name on one element. So a file with two columns called "id" converts cleanly to elements and needs one of them renamed to id_2 as attributes. This tool does that rename only in attribute mode, and says which column it renamed.
Is there an XML template mode with placeholders?
No, and that is deliberate. FreeFormatter required you to write a template with ##1## placeholders because it did not read your header row at all — the template was how you told it what the columns were called. Here the header row is the default and does that job, so a template would be a second way to say the same thing. If a real case turns up that the header row cannot express, it gets built then.
Does my file get uploaded anywhere?
It does not. Naming the elements and writing the document are ordinary function calls executing inside this tab, using the file-reading API every browser has built in; nothing on this domain will accept a file even if you point it at one. Watch your network panel while you drop a spreadsheet and you will see it stay quiet. Two of the four converters ranking above this page for the same search send your rows somewhere first, and one of them announces it in its opening line. Since a spreadsheet on its way into XML is generally an order feed, a product catalog or a staff list, that distinction is the only one worth caring about.