JSON vs XML: which one I actually reach for, and why

Not a spec comparison. The three questions I ask when choosing between them, why XML is not dead, and the one JSON limitation that catches people out in production.

Every few months a junior engineer asks me some version of "why would anyone still use XML?" — usually with the tone of someone who has already decided. It is a fair question, and the honest answer is more interesting than "XML is legacy, JSON won".

They solve overlapping but genuinely different problems. Here is how I actually decide. (If you landed here just trying to read a file you were sent, start with opening a JSON file or opening an XML file instead.)

The same data, both ways

Start concrete. Two mobile subscribers, as JSON:

subscribers.json
{
  "subscribers": [
    {
      "msisdn": "447700900142",
      "plan": "Unlimited 5G",
      "roaming": true,
      "rsrp": -92
    },
    {
      "msisdn": "447700900458",
      "plan": "Pay As You Go",
      "roaming": false,
      "rsrp": -104
    }
  ]
}

The same thing as XML:

subscribers.xml
<?xml version="1.0" encoding="UTF-8"?>
<subscribers>
  <subscriber msisdn="447700900142">
    <plan>Unlimited 5G</plan>
    <roaming>true</roaming>
    <rsrp unit="dBm">-92</rsrp>
  </subscriber>
  <subscriber msisdn="447700900458">
    <plan>Pay As You Go</plan>
    <roaming>false</roaming>
    <rsrp unit="dBm">-104</rsrp>
  </subscriber>
</subscribers>

The XML is longer. That is the criticism everyone leads with, and it is true but mostly irrelevant — gzip flattens the difference to almost nothing over the wire.

Look instead at unit="dBm". There is nowhere natural to put that in the JSON version. You would have to invent "rsrp_unit": "dBm" as a sibling field, or nest an object, and either way you are inventing a convention that every consumer has to learn. XML has a built-in place for "information about this value, as opposed to the value itself". That distinction is the real difference between the two formats.

The differences that actually matter

JSONXML
Data typesStrings, numbers, booleans, null, arrays, objectsEverything is text until a schema says otherwise
Metadata on a valueNo native concept — you invent a conventionAttributes, built in
CommentsNot allowed. At all.Yes
Schema validationJSON Schema — good, but bolted on and optionalXSD — mature, ubiquitous, often mandatory
Mixed content (text with inline markup)PainfulWhat it was designed for
Querying a documentJSONPath, or parse it allXPath — genuinely powerful
NamespacesNone. Key collisions are your problem.Built in
Parse in a browserJSON.parse(), one lineDOMParser, more ceremony

The three questions I ask

1. Who is on the other end?

If it is a browser, JSON, and there is no real debate. JSON.parse() gives you a native object in one line; XML gives you a DOM you then have to walk. If the other end is a bank, an insurer, a government system, or telecoms billing, you will probably be handed an XSD and told which XML to produce. That decision was made years before you arrived.

2. Does the document need to describe itself?

This is the one people underweight. If a file has to be validated by someone else, years from now, without access to your source code, XML plus an XSD carries its own contract. "Is this file valid?" has a definitive answer that a stranger can check with off-the-shelf tooling.

JSON Schema does exist and it is good. But it lives beside the document, not with it, and in practice it is far more often missing than present.

3. Is a human going to edit this by hand?

Then neither, ideally — reach for YAML or TOML. But if forced to choose, XML wins for one unglamorous reason: you can leave comments. Config files without comments become archaeology. I have lost real hours to a JSON config with a mystery flag and no way to record why it was there.

The JSON limitation that catches people out

JSON numbers have no defined precision. RFC 8259 §6 describes the grammar, not what a parser must do with it, and nearly every JavaScript parser reads them as IEEE-754 doubles. That gives you 53 bits of integer precision.

Try this in any browser console
JSON.parse('{"id": 9007199254740993}').id
// 9007199254740992   <-- note the last digit

The value changed. Silently. No error, no warning. If your IDs are 64-bit integers — Twitter/X snowflake IDs, many database bigints, some IMSI-derived keys — JSON will quietly corrupt them past 2^53.

The fix is to send large identifiers as strings. It looks wrong the first time and it is absolutely correct:

{ "subscriberId": "9007199254740993" }

Converting between them

Sooner or later you will need to bridge the two, and the conversion is lossy in one direction. XML to JSON has to make judgement calls that have no single right answer:

  • Attributes vs elements. Does msisdn="447700900142" become a normal key, or something prefixed like @msisdn? Both conventions exist.
  • Single vs repeated elements. One <subscriber> looks like an object; two look like an array. A converter seeing only one cannot know whether more were possible.
  • Whitespace. Meaningful in mixed content, noise everywhere else.

That second one causes real production bugs: code works against a test file with three records, then breaks against live data with exactly one, because the shape changed from array to object underneath it.

XML to JSON Converts in the browser so you can see exactly which choices it makes on your data before wiring it into anything. JSON to XML The other direction, when a partner system needs XML from your JSON API.

So which one?

  • Web API talking to a browser or mobile app → JSON. Not close.
  • Regulated or enterprise integration → XML, and you will likely be told so.
  • Config a human edits → YAML or TOML. XML over JSON if those are off the table, purely for comments.
  • Documents with inline markup → XML. This is its home ground.
  • Anything with 64-bit IDs → either, but send those IDs as strings.

If XML is the side you are less sure about, this explainer covers attributes, namespaces and schemas in more depth. XML is not legacy. It is specialised, and its specialisations — schemas, namespaces, attributes, XPath — are exactly the things you stop needing the moment your consumer is a browser. Which is most of the time now. That is the whole story of why JSON took over, and also why XML never actually left.