Convert XML to NDJSON

Converting XML to NDJSON writes the document as compact JSON on a single line, because an XML file has exactly one root element and newline-delimited JSON splits on a top-level list. That makes the pair right when each file is one record, and wrong when the records are a repeated element inside the document — which this page explains how to handle.

  • Where it runs In your browser. The file is never uploaded.
  • Rebuilt NDJSON works differently from an XML, so this is not the gradual degradation a lossy codec applies. What NDJSON can express is reproduced faithfully; what it has no equivalent for does not survive at all.
  • File size limit Up to 100 MB per file, free, without an account.
  • Worth knowing XML attributes and text nodes both become keys, which is a judgement call the converter makes for you.

Up to 100 files at once. Mixed formats are fine.

Why an XML document always becomes exactly one line

Newline-delimited JSON needs something to split on, and the split here happens at a list on the outside of the parsed data. Parsing XML never produces one: the result is always an object with a single key, the root element name, holding everything else. One value in, one line out.

That holds however large the file is and however many repeated elements it contains. A 40 MB export of ten thousand records converts into a single 40 MB line. Nothing is lost and nothing is split, and if what you needed was ten thousand lines, this conversion has not done it.

Getting one line per record out of an XML export

The route that works is two steps. Convert the file to JSON, then run it through jq: select the path holding the repeated element, iterate it, and use the compact output flag so each object lands on its own line. It is one command, it is repeatable in a script, and it puts the choice of what counts as a record where it belongs — with you.

No converter can make that choice from the document alone. In an RSS feed the record is the item element; in a SOAP response it might be a row three levels inside the body; in a bank export it is whatever the vendor decided to call a transaction. A tool that guessed would be right often enough to be trusted and wrong often enough to corrupt a load.

The cases where one line per file is exactly right

This is not a pair with no use. If your inputs are many small XML documents rather than one large one — a directory of invoices, a folder of manifests, a batch of per-event payloads — then one line per file is precisely the shape a loader wants, and concatenating the outputs produces a valid NDJSON stream with one record per source document.

It also works for inventory. Converting each config file in a repository and appending the lines gives you a queryable dataset of what those files contain, which is a genuinely useful thing to have and is hard to get any other way. In both cases the conversion is doing its job and the single line is the feature.

The shape problem that follows an XML record into JSON

Even after you have split the records, one XML behaviour follows them. A repeated element becomes an array only when it repeats: a record containing one tag element produces a string, and a record containing two produces a list of strings. Within a single export, some lines will have the array and some will not.

A schema-on-read store will infer a type from the first records it sees and then reject or coerce the rest. The fix belongs in the jq pass rather than in the loader: normalise every field that can repeat into an array as you emit each line, so the stream is uniform before anything downstream sees it.

What the line actually contains

Compact JSON with no indentation, keys in document order, terminated by a newline. Attributes appear as keys prefixed with @ and element text on a tag that also has attributes appears under #text. A namespace prefix stays inside the key name, so soap:Body is a key with a colon in it, and a document with an XML declaration carries an extra top-level key named ?xml.

Those last two are worth stripping before a load. Field names containing colons and question marks are awkward or invalid in a good number of query engines and table schemas, and the declaration is metadata about the file rather than data from it.

Types as they arrive from an XML parser

Values that look numeric are parsed, in attributes and element text alike. That is convenient for a count and destructive for identifiers: an order reference written 007 arrives as the number 7, and a version attribute written 1.0 arrives as 1. In a data store those become numeric columns and the padding is gone from every row.

One case behaves better than you might fear: an integer too long to survive as a JSON number is left as a string rather than rounded, so a nineteen-digit reference comes through intact. Scientific notation does not get that protection — a value written 1e3 arrives as 1000. Nothing raises an error either way. If the export contains reference numbers you will join on later, cast them back to strings in the jq pass, before the first load rather than after.

Loading the result, and the line length nobody plans for

NDJSON is read directly by jq, by pandas with its lines option, and as a load format by the common warehouse loaders. Elasticsearch bulk needs an action line before each document, which is added in the same jq pass that splits the records.

The thing to plan for with this pair specifically is line length. A reader that processes a line at a time has to hold that entire line in memory, so a whole-document line is a whole-document buffer, and any per-record size limit in the destination applies to the complete file rather than to a record. That is the practical reason to split the records before loading rather than after.

When a streaming XML parser is the right answer instead

If the export is large, arrives regularly, and has to become a stream every time, the honest recommendation is not a browser converter at all. A streaming parser reads an XML file element by element without building the whole tree in memory, and emitting one JSON line per record from that loop is a short program in any language.

That approach also survives files bigger than memory, which nothing on this page does — parsing here builds the entire document as JavaScript objects before a single byte is written. For a one-off file, or for the per-file cases above, the converter is faster than writing the program. For a recurring pipeline it is not.

Comments, and what a line-delimited file cannot hold

XML comments are dropped during parsing and NDJSON has no comment syntax to receive them, so any documentation inside the export is gone. There is also no header, no schema and no preamble: every line in an NDJSON file is a record, and a loader will try to read anything at the top of the file as one.

If the load needs to record where the data came from, that belongs in a field on the record or in the filename. Adding it as a first line makes the file invalid for its purpose, and the loader will tell you so in a way that is much less clear than this paragraph.

How to convert XML to NDJSON

  1. Drop your XML file onto this page, or click to choose one.
  2. The document is written as one compact JSON line in your browser.
  3. Download it, or convert to JSON first if you need one line per record.

XML against NDJSON: one rooted document against a line stream

XML compared with NDJSON
XMLNDJSON
Full nameExtensible Markup LanguageNewline-Delimited JSON
File extension.xml.ndjson, .jsonl
Media typeapplication/xmlapplication/x-ndjson
First published19982013
Published byW3C
SpecificationXML 1.0
LicensingOpen standardOpen standard
Standing todayCurrentCurrent
Opens in a browserEvery browserNo browser
Considered insteadJSON, YAMLJSON, CSV

What is lost

Comments do not survive. XML lets you annotate a file and NDJSON has no syntax for it, so every explanatory line is dropped — which matters most on exactly the files people comment: configuration somebody else has to maintain.

Opening the result

No browser reads NDJSON. It is the less portable of the two, so it is worth being sure the program at the other end accepts it before sending one.

The usual programs do not overlap: XML opens in Visual Studio Code and oXygen XML Editor, NDJSON in jq and pandas — so whoever receives the result needs something from the second list.

What each format is for

XML is W3C's format, published in 1998. The specification is XML 1.0, and it is worth reading if the file has to outlive the tool that wrote it.

NDJSON dates from 2013. jq and pandas all read it.

XML was published in 1998 and NDJSON in 2013. The older one is generally the safer file to hand to somebody; the newer one usually does the job in fewer bytes.

XML to NDJSON: questions about loading an export

Are my XML files uploaded anywhere?

No. This conversion runs entirely inside your browser, so the file never leaves your device. You can confirm it yourself: open the network tab of your browser's developer tools and convert something. You will see the page load, plus the analytics and advertising the site is paid for with — and nothing carrying your file.

Why is the output a single line?

Because an XML document has exactly one root element, and the record boundary here is a list at the top level of the parsed data. The root object is one value, so it is written as one line — even when the interesting records are a repeated element inside it.

How do I get one line per record?

Convert to JSON instead, then use jq to select the repeated element and emit it compactly, one object per line. That is a single expression and it lets you name which element is the record, which no converter can guess.

Is the conversion still useful?

Yes, when the whole document is the record. A per-file inventory, a batch of small documents concatenated after conversion, or a pipeline where each XML file is one event all work exactly as intended.

What happens to attributes?

They become keys prefixed with @ inside the object, alongside the child elements. An element with its own text as well as attributes carries that text under a key named #text.

Will one record and many records produce the same keys?

No. A repeated element becomes an array only when it appears more than once, so a document with one entry produces an object where a document with two produces a list. Any consumer has to handle both shapes.

Does the export get uploaded?

No. The parsing and the serialisation both run in this page in JavaScript, so an export full of customer records stays on your machine. The free tier accepts up to 100 MB.

More about these formats