JSON or XML today: migration and coexistence

How the data models differ, what has no JSON equivalent, converting between the formats without losing meaning, and a checklist for running both behind one API.

Two data models, not two syntaxes

CapabilityXMLJSONConsequence
AttributesYes, distinct from contentNo, keys onlyAttribute data becomes an ordinary field
Order of fieldsSignificantNot significant in the modelOrder-dependent logic breaks
Repeated siblingsA list of elementsAn array — or one object if there is only oneThe single-item ambiguity is the classic JSON trap
Mixed contentSupportedImpossibleDocuments with inline markup do not convert
NamespacesBuilt inNoneCollisions appear when vocabularies merge
CommentsSupportedNot in the standardComments are lost
SchemaXSD, DTD, RELAX NGJSON SchemaDifferent expressive power both ways
DatatypesRich, with facetsA small set plus conventionsNumbers and dates need a contract
Validation errorsLine and columnInstance pathTooling differs a lot
<!-- XML: the language is an attribute, the text is content -->
<message lang="en">Hello</message>

<!-- JSON has no place for the attribute except as a key -->
{ "lang": "en", "text": "Hello" }

The single-item list ambiguity is the most damaging difference. A converter cannot know whether one <item> should become an array of one or an object, and guessing wrong breaks every consumer.

Converting without losing meaning

<!-- a convention that survives a round trip: mark attributes and types -->
<order id="1">
  <placedAt type="date">2026-09-18</placedAt>
  <items>
    <item sku="A1" qty="2"/>
  </items>
</order>
{
  "@id": "1",
  "placedAt": "2026-09-18",
  "placedAtType": "date",
  "items": { "item": [ { "@sku": "A1", "@qty": "2" } ] }
}
  1. Decide a convention for attributes and for single-item lists, and write it down before converting anything.
  2. Preserve order where the consumer depends on it, or change the consumer first.
  3. Convert types explicitly; everything in XML is a string until your mapping says otherwise.
  4. Keep the original XML alongside the converted form during the transition, so a wrong conversion is reversible.
  5. Round-trip a representative fixture and diff both directions. A conversion that is not round-trip stable is not done.
  6. Publish the new format to one consumer, compare behaviour, then widen.

Running both behind one API

GET /v2/orders/1024 HTTP/1.1
Accept: application/xml;q=0.9, application/json;q=1.0

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Vary: Accept

{"id":"1024","total":{"amount":"19.99","currency":"EUR"}}
  • One internal model, two serialisers. Never let either wire format become the domain model.
  • Negotiate with Accept and set Vary: Accept so caches do not mix the representations.
  • Generate both from the same contract — an XSD with a JSON Schema, or a single definition with two generators.
  • Keep error shapes parallel so a client that switches format sees the same fields.
  • Measure real usage before removing either; some consumers will still be on the old format years later.
  • Set a removal date and a deprecation header so the last consumer has a deadline, not a surprise.
def serialise(order, media_type):
    if media_type == "application/json":
        return json.dumps(to_dict(order)), "application/json"
    if media_type == "application/xml":
        return to_xml_string(order), "application/xml"
    raise UnsupportedMediaType(media_type)

def to_dict(order):
    """The internal model is the source of truth for both formats."""
    return {
        "id": str(order.id),
        "total": {"amount": f"{order.total:.2f}", "currency": order.currency},
    }
💡
Keep XML where it is genuinely better: documents with mixed content, XSD-validated regulated interchange, SOAP integrations you do not control, and formats such as SVG, RSS and XHTML where the ecosystem is the standard. Do not migrate for novelty.

FAQ

Can every XML document be converted to JSON?
No. Mixed content, comments, processing instructions and multiple namespaces have no natural JSON form. For document-shaped XML, keep it as XML and convert only the record-shaped parts.
Is JSON always smaller?
Usually, because there are no repeated closing tags. For deeply nested data with long field names repeated per record, a binary format or a columnar layout beats both.

SVG, XHTML and XML in the browser Attributes vs child elements: modelling decisions

Last refreshed 2026-09-18.