XML in healthcare
Software & Tech Stackarticle · 10 min · updated Jul 19, 2026

XML in healthcare

By Rajendra Sharma, RN, CPC, CPBReviewed by Rajendra Sharma, RN, CPC, CPB · Jul 19, 2026

The format that carries the clinical document. Elements, attributes, namespaces, schema validation and XPath — and an honest account of what XML does that JSON cannot.

XMLXSDXPathHL7 CDAFHIRIHE

In one line

XML is a text format for tree-shaped data with a strong sense of structure — and in health IT it is not legacy trivia, because the clinical document you are handed on a referral, a discharge, or a health-information exchange query is very often XML.

Elements and attributes

An XML document is a single tree. Every branch is an element, written as a start tag and an end tag. Elements can carry attributes — name/value pairs on the start tag.

<patient status="active">
  <mrn>MRN-4471882</mrn>
  <name>
    <given>Karen</given>
    <family>Whitfield</family>
  </name>
  <birthDate>1962-11-03</birthDate>
</patient>

When should something be an attribute rather than an element? The working rule: attributes carry metadata about the thing (status, unit, codeSystem); elements carry the thing itself. It matters practically, because attributes cannot repeat and cannot nest. A patient has one status; a patient may have several addresses, so addresses must be elements.

Two health standards make opposite choices here, and knowing that saves you confusion. FHIR's XML form puts almost every value in a value attribute; CDA puts clinical values in attributes on typed elements. Neither is wrong — they are different traditions.

Namespaces, and why they are not optional

Two organisations both invent an element called <code>. Merge their documents and a parser cannot tell which is which. Namespaces fix that by binding element names to a URI, declared with xmlns.

<Patient xmlns="http://hl7.org/fhir">
  <id value="meera-iyer"/>
  <identifier>
    <system value="http://example.org/fhir/sid/abha"/>
    <value value="91-7412-8523-6900"/>
  </identifier>
  <name>
    <family value="Iyer"/>
    <given value="Meera"/>
  </name>
  <gender value="female"/>
  <birthDate value="1989-04-12"/>
</Patient>

The FHIR XML namespace is http://hl7.org/fhir. Get this exactly right. It is a namespace identifier, not an address you fetch — the fact that it looks like a URL is incidental. A commonly repeated error writes it as urn:hl7-org:fhir, which is not a FHIR namespace at all. The URN you may be thinking of is urn:hl7-org:v3, which is the HL7 version 3 / CDA namespace — a different standard entirely. Mix them up and your validator rejects a perfectly good document, or worse, silently matches nothing.

A namespace can be declared as the default (as above, applying to that element and its descendants) or bound to a prefix, e.g. xmlns:cda="urn:hl7-org:v3" and then <cda:id/>. The prefix itself is arbitrary and carries no meaning — only the URI it maps to counts. Code that matches on the prefix string instead of the namespace URI is a bug waiting for the first sender who picks a different letter.

Well-formed vs valid

These are two different bars, and people conflate them constantly.

Well-formed is syntax. One root element; every start tag closed; tags nested, not overlapping; attribute values quoted; the five reserved characters escaped (&amp;, &lt;, &gt;, &quot;, &apos;). A non-well-formed document is not XML — parsers must reject it outright.

Valid is conformance to a declared model — usually an XSD schema. Valid means the right elements, in the right order, the right number of times, with the right datatypes.

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           elementFormDefault="qualified">
  <xs:element name="patient">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="mrn" type="xs:string"/>
        <xs:element name="birthDate" type="xs:date"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

That schema says: a patient contains exactly one mrn then exactly one birthDate, and birthDate must parse as a date. A document with the two in the wrong order is well-formed and invalid. Note what you got for free: ordering and typing enforced by the parser, before your code runs. That is XML's real gift, and JSON has no equivalent built in.

Schema-valid still isn't clinically correct. CDA validation is layered: XSD first, then Schematron rules for the C-CDA template constraints (cardinality, required codes, terminology bindings). A document can pass XSD and fail Schematron badly.

XPath

XPath is a path language for pulling nodes out of a tree — the thing you will actually spend your time on when handed a 4,000-line C-CDA.

//cda:ClinicalDocument/cda:recordTarget/cda:patientRole/cda:id/@extension
//cda:observation[cda:code/@code='29463-7']/cda:value/@value

The first pulls the patient identifier attribute. The second pulls a body-weight value — LOINC 29463-7 is "Body weight", and it is a LOINC code, not SNOMED CT; the two are routinely mislabelled in slide decks. XPath predicates in [...], @ for attributes, // for "anywhere below". Your XPath engine must be told the namespace-to-prefix mapping separately — this is where most first attempts return an empty node set.

Where XML actually lives in health IT

  • HL7 CDA and C-CDA — the clinical document. Namespace urn:hl7-org:v3. This is XML's stronghold and it is not going away; US certified EHRs still exchange C-CDA at scale.
  • FHIR — XML is a first-class serialisation alongside JSON. Same resource model, same field names, two wire formats. Content type application/fhir+xml.
  • IHE profiles — XDS metadata, cross-enterprise document sharing, ebXML registries.
  • SOAP web services — still the transport in many national and hospital integrations.
  • DICOM has an XML representation, and CCDA-on-FHIR, X12 wrappers and government reporting formats all touch XML somewhere.

A C-CDA header, to show what the tradition looks like:

<ClinicalDocument xmlns="urn:hl7-org:v3">
  <realmCode code="US"/>
  <typeId root="2.16.840.1.113883.1.3" extension="POCD_HD000040"/>
  <templateId root="2.16.840.1.113883.10.20.22.1.1" extension="2015-08-01"/>
  <id root="2.16.840.1.113883.19.5" extension="MRN-4471882"/>
  <code code="34133-9"
        codeSystem="2.16.840.1.113883.6.1"
        displayName="Summary of episode note"/>
  <title>Continuity of Care Document</title>
  <effectiveTime value="20260719103000-0400"/>
</ClinicalDocument>

Everything is an attribute; 2.16.840.1.113883.6.1 is the OID for LOINC; the templateId extension 2015-08-01 marks the C-CDA R2.1 US Realm Header.

XML vs JSON, honestly

JSON is lighter, maps straight onto objects in every language, and is what a modern API should default to. But XML carries three things JSON does not have natively:

  1. Mixed content — text and markup interleaved. <p>Take <content styleCode="Bold">two tablets</content> daily</p> is how a CDA narrative block preserves the human-readable document with emphasis and internal links intact. JSON has no clean way to express markup inside a run of text, which is why FHIR's Narrative.div is a blob of escaped XHTML inside the JSON.
  2. Attributes as a separate axis — metadata attached to a value without inventing a wrapper object.
  3. A mature validation and query stack — XSD, Schematron, XPath, XSLT, and digital signatures over a canonical form. JSON Schema is good and improving; XSLT has no real JSON counterpart, which is why document transformation pipelines are still XML.

If you are exchanging a document — a signed, human-readable, legally-attested artefact — XML's model fits. If you are exchanging data for an app to render, JSON is less work. This is why FHIR supports both and lets the use case decide.

The errors you will actually hit

  • Wrong or missing namespace. The single most common failure. Your XPath returns nothing and everything looks right.
  • Unescaped &. A patient address containing Smith & Sons Clinic breaks the parse. Escape it or wrap it in <![CDATA[...]]>.
  • Encoding mismatch. Declaring <?xml version="1.0" encoding="UTF-8"?> while emitting a different encoding mangles every non-ASCII name.
  • XXE — XML External Entity injection. A hostile document declares an external entity and your parser dutifully reads /etc/passwd or makes network calls. Disable external entity resolution and DTD processing on every parser handling third-party XML. This is a real, exploited vulnerability class in health integrations, not a theoretical one.
  • Assuming element order is flexible. In XSD <xs:sequence> it is not.
  • Treating whitespace as free. Between elements it is usually ignorable; inside a text node it is content.

Reading about namespaces is not the same as debugging one. The data-formats lab puts a real C-CDA and a FHIR XML resource in front of you to parse, namespace-map, XPath and validate — go break them there.

References

  1. W3C — Extensible Markup Language (XML) 1.0
  2. W3C — XML Schema Part 0: Primer
  3. HL7 FHIR R4 — XML Representation
  4. HL7 — Consolidated CDA R2.1

Related entries