JSON and XML both exist to move structured data between systems, but they come from different worlds. XML grew out of document markup (SGML) in the late 1990s and was built to describe rich, marked-up text. JSON came out of JavaScript a few years later and was built to serialize plain data objects. That origin gap explains almost every practical difference you will run into.
This guide compares the two head-to-head: how they model data, how verbose they are, how you validate them, and where each one is still the right call. We use the same record in both formats throughout so the differences stay concrete. If you need to move data between the two, the JSON ⇄ XML converter handles the round trip and the caveats we cover below.
The Same Data in Both Formats
The fastest way to feel the difference is to look at one record. Here is a customer with an id, a name, two roles, and a nested address, written as JSON:
{
"id": 4021,
"name": "Ada Lovelace",
"active": true,
"roles": ["admin", "editor"],
"address": {
"city": "London",
"postcode": "NW1 6XE"
}
}Now the same record as XML. Every value is wrapped in an opening and closing tag, the array becomes repeated elements, and there is no built-in way to tell that 4021 is a number or that active is a boolean — everything is text:
<?xml version="1.0" encoding="UTF-8"?>
<customer>
<id>4021</id>
<name>Ada Lovelace</name>
<active>true</active>
<roles>
<role>admin</role>
<role>editor</role>
</roles>
<address>
<city>London</city>
<postcode>NW1 6XE</postcode>
</address>
</customer>The XML version is roughly twice the byte count for identical information, and the array had to be modeled by choosing a wrapper element (roles) and a repeated child (role) — a decision JSON makes for you with square brackets.
Data Model: Types, Attributes, and Namespaces
The deepest difference is not syntax, it is the data model each format assumes.
JSON has a small, typed model
JSON defines six value types: string, number, boolean, null, object, and array. A parser hands them back as native language values — a JSON number becomes a float or int, true becomes a real boolean, and an array becomes a list. There is nothing to interpret. See JSON Data Types Explained for the full breakdown.
XML has one type — text — plus structure
In plain XML, every leaf is character data. Whether <id>4021</id> is a number is a question your application (or an attached schema) has to answer; the format itself only sees a string. In exchange, XML gives you structural features JSON simply does not have:
- Attributes — metadata attached to an element, like
<price currency="USD">42.00</price>. JSON has no attribute concept; everything is a key. - Namespaces — prefixes like
xsl:orsoap:that let documents from different vocabularies coexist without name collisions. - Mixed content — text and elements interleaved, as in
<p>See <b>this</b> note</p>. This is the document use case XML was designed for. - Comments and processing instructions — part of the spec, whereas standard JSON has no comments at all.
Verbosity, Payload Size, and Readability
XML's closing tags and structural wrappers make it consistently larger than equivalent JSON — often 1.5x to 2x the raw bytes for data-shaped content. On the wire this matters less than it used to, because HTTP compression (gzip or brotli) collapses XML's repetitive tags very effectively; a gzipped XML payload is far closer to gzipped JSON than the raw numbers suggest.
Where verbosity still bites is human ergonomics and CPU. Repeated open/close tags are noisier to read and write by hand, and larger raw payloads mean more bytes to parse and more allocations before compression ever enters the picture. For a chatty API returning thousands of small objects, JSON's compactness compounds. If you are hand-tuning payloads, the JSON Minifier strips whitespace from JSON, though the bigger structural savings come from the format choice itself.
| Aspect | JSON | XML |
|---|---|---|
| Primary purpose | Data interchange | Document markup (and data) |
| Value types | 6 native types | Text only (typed via schema) |
| Attributes | No | Yes |
| Namespaces | No | Yes |
| Comments | No (standard JSON) | Yes |
| Mixed content | No | Yes |
| Typical size | Baseline | ~1.5–2x larger raw |
| Schema language | JSON Schema | XSD, RELAX NG, DTD |
| Query language | JSONPath, JQ | XPath, XQuery |
| Native fit | JavaScript, most langs | Document tooling, .NET, Java |
Parsing and Language Support
Both formats are parsed everywhere, but the developer experience differs. JSON parses into native data structures in one call, with no object model to navigate:
const data = JSON.parse(jsonString);
console.log(data.roles[0]); // "admin"
console.log(typeof data.id); // "number"
console.log(data.active === true); // trueXML parsing typically returns a DOM tree or fires SAX events, and you walk it with a query language like XPath. The same field access is more ceremony, and each value comes back as a string you must cast yourself:
import xml.etree.ElementTree as ET
root = ET.fromstring(xml_string)
first_role = root.find("roles/role").text # "admin"
customer_id = int(root.find("id").text) # cast "4021" -> 4021
is_active = root.find("active").text == "true" # cast to boolThat casting step is the practical tax of XML's text-only model: the parser cannot know 4021 is a number or that active is boolean, so your code carries that knowledge. In JSON, working with the parsed result is just working with normal objects and arrays.
Schema and Validation: XSD vs JSON Schema
Both formats can enforce structure. XML uses XSD (XML Schema Definition), a mature and expressive standard that also carries data types — an XSD can declare that <id> is an xs:integer, which is how typed XML recovers what JSON gives you for free. RELAX NG and the older DTD are alternatives.
JSON uses JSON Schema, itself a JSON document describing the shape, types, and constraints of your data. Here is a minimal schema for our customer record:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["id", "name", "roles"],
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"active": { "type": "boolean" },
"roles": {
"type": "array",
"items": { "type": "string" }
},
"address": {
"type": "object",
"properties": {
"city": { "type": "string" },
"postcode": { "type": "string" }
}
}
}
}Practically, XSD is more powerful for document constraints (element ordering, occurrence counts, rich built-in types) while JSON Schema is lighter and reads like the data it validates. You can generate a starting schema from a sample with the JSON Schema Generator, then tighten it by hand. For a deeper walkthrough see the JSON Schema guide.
Where Each Format Still Wins
Reach for XML when
- You are producing documents — anything with mixed content, formatting, or prose structure. XHTML, DOCX, SVG, and RSS are all XML-family for good reason.
- You must speak SOAP, or integrate with enterprise, government, healthcare (HL7), or financial systems where XML is the entrenched standard.
- You need namespaces to combine vocabularies, or rich validation and transformation via XSD and XSLT.
- Your data is genuinely tree-shaped markup rather than records.
Reach for JSON when
- You are building a web or mobile API. JSON is the default for REST and the payload format for GraphQL responses.
- You work primarily in JavaScript/TypeScript, where JSON maps to objects with zero friction.
- You want compact, readable config or data that humans edit and machines consume.
- You value simplicity — fewer features means fewer footguns and a smaller mental model.
A useful heuristic: if you would describe your payload as a record or a list of records, JSON fits. If you would describe it as a document, XML fits. Most modern application data is records, which is why JSON dominates new development while XML persists in documents and established protocols.
Converting Between JSON and XML (and the Caveats)
Conversion is common — wrapping a legacy SOAP service in a JSON API, or feeding an XML feed into a JSON pipeline. The catch is that there is no single official mapping between the two, because their data models do not line up one-to-one. Watch three specific gaps:
- Attributes. XML attributes have no JSON equivalent, so converters invent a prefix — commonly
@— turning<price currency="USD">into a key like"@currency": "USD". Round-tripping only works if both sides agree on that convention. - Arrays. XML has no array type; it uses repeated elements. A single
<role>looks the same as one item, so a converter cannot always tell “array of one” from “scalar.” You often need a schema or an explicit rule to force list semantics. - Types. Because XML leaves are text,
<id>4021</id>converts to the string"4021"unless the converter is told (or guesses) it is a number. Guessing can be wrong — a zip code like"01234"must stay a string.
If your data is tabular rather than tree-shaped, converting to a flat format may serve you better than XML — the JSON to CSV converter covers that case, and JSON vs CSV explains when to choose it.