Overview
Why a valid-looking CDA returns zero XPath nodes
The published CDA core model identifies the XML namespace for ClinicalDocument as urn:hl7-org:v3.[1] A typical document makes that namespace the default:
<ClinicalDocument xmlns="urn:hl7-org:v3">
<id root="2.16.840.1.113883.999.1" extension="DOC-001"/>
<component>
<structuredBody>
<component>
<section>
<title>Synthetic Summary</title>
</section>
</component>
</structuredBody>
</component>
</ClinicalDocument>
The element is written as ClinicalDocument, but its expanded name is the pair {urn:hl7-org:v3}ClinicalDocument. The same applies to its unprefixed descendant elements while the default namespace remains in scope.
An XPath such as //ClinicalDocument/component/structuredBody commonly returns an empty result in XPath 1.0 because each unprefixed name test means “this local name in no namespace”. The query does not inherit the default namespace declared by the XML document.[3]
XML identity
An element name contains a namespace URI and a local name
Namespaces in XML defines an expanded name as a namespace name combined with a local name.[2] Prefixes are a compact way to bind those names in a document; the prefix text is not the namespace identity.
| XML form | Namespace URI | Local name | Same expanded name? |
|---|---|---|---|
<ClinicalDocument xmlns="urn:hl7-org:v3"> | urn:hl7-org:v3 | ClinicalDocument | Yes |
<cda:ClinicalDocument xmlns:cda="urn:hl7-org:v3"> | urn:hl7-org:v3 | ClinicalDocument | Yes |
<ClinicalDocument> | None | ClinicalDocument | No |
<x:ClinicalDocument xmlns:x="urn:example:extension"> | urn:example:extension | ClinicalDocument | No |
This is why changing cda: to another prefix does not change the element's namespace-aware identity when the new prefix is bound to the same URI. Removing the namespace declaration or binding the prefix to a different URI does change that identity.
Default namespace
The default applies to elements, not ordinary attributes
A default namespace declaration applies to unprefixed element names within its scope. It does not apply directly to unprefixed attribute names.[2] In the following CDA fragment, id is in the CDA namespace, while root and extension are in no namespace:
<id root="2.16.840.1.113883.999.1" extension="DOC-001"/>
A namespace-aware XPath therefore uses a prefix for the element but not for those attributes:
//cda:id/@root
//cda:id/@extension
Do not automatically prefix every attribute. //cda:id/@cda:root looks for an attribute named root in the CDA namespace, which is not the same as the ordinary unprefixed root attribute.
Namespace declarations are scoped. A descendant can bind a new default namespace, change an existing prefix or clear the default namespace with xmlns="". Inspect the declarations in scope at the failing node rather than assuming that the root declaration applies unchanged to the whole document.
XPath 1.0
Bind a query prefix to the CDA namespace
XPath 1.0 states that an unprefixed QName in a node test has a null namespace URI; the XML document's default xmlns declaration is not used for that query name.[3] The reliable correction is to bind a prefix in the XPath evaluation context and use it for CDA elements.
/cda:ClinicalDocument/cda:component/cda:structuredBody
/cda:component/cda:section/cda:title
The query prefix is chosen by the query. The source document does not need to use the same prefix, or any prefix at all. The important mapping is:
cda → urn:hl7-org:v3
For example, a browser-based XPath 1.0 evaluation can supply a resolver:
const namespaces = {
cda: 'urn:hl7-org:v3',
sdtc: 'urn:hl7-org:sdtc'
};
const resolver = prefix => namespaces[prefix] || null;
const result = xmlDoc.evaluate(
'//cda:section/cda:title',
xmlDoc,
resolver,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
If the expression contains cda: but the resolver does not define it, the processor should report an unresolved prefix or expression error rather than treating it as the document's default namespace.
XPath versions
XPath 2.0 and 3.1 can define a default element namespace
Later XPath versions include a default element/type namespace in the static expression context. An unprefixed element name test can use that namespace when the host language or processor configures it.[4] This behaviour is not a reason to assume that every XPath environment will interpret //section as CDA.
XPath 3.1 also supports URI-qualified names such as:
//Q{urn:hl7-org:v3}section/Q{urn:hl7-org:v3}title
This form states the URI directly and avoids a separate prefix mapping, but an XPath 1.0 processor will not recognise it. Record the XPath version and host-language behaviour as part of the interface configuration.
Extensions
CDA documents can contain more than one namespace
The CDA core model uses urn:hl7-org:v3 for base CDA elements and also identifies elements in extension namespaces such as urn:hl7-org:sdtc.[1] A document can also contain locally defined or implementation-guide-specific extensions.
<ClinicalDocument
xmlns="urn:hl7-org:v3"
xmlns:sdtc="urn:hl7-org:sdtc"
xmlns:ext="urn:example:clinical-extension">
<id root="2.16.840.1.113883.999.1"/>
<sdtc:statusCode code="active"/>
<ext:processingNote>Synthetic example</ext:processingNote>
</ClinicalDocument>
Each namespace needs its own query mapping when you need to select its elements:
//cda:id
//sdtc:statusCode
//ext:processingNote
Do not treat every prefixed element as invalid CDA or silently treat every extension as a base CDA element. Confirm the applicable CDA implementation guide and extension rules before deciding whether the extra namespace is permitted.
Diagnostic fallback
Use local-name() to investigate, not as the default query strategy
A namespace-agnostic expression can confirm that matching local names exist:
//*[local-name() = 'section']
This is useful when the namespace is unknown or unexpectedly redeclared. However, it can match section elements from CDA, an extension vocabulary or a namespace-free fragment. That can hide the underlying namespace error and select the wrong data.
If a temporary diagnostic query must restrict the namespace, include both parts of the expanded name:
//*[local-name() = 'section'
and namespace-uri() = 'urn:hl7-org:v3']
A properly bound cda:section expression is shorter and normally clearer for production mappings.
Failure patterns
What common namespace failures look like
| Observed result | Likely cause | What to verify |
|---|---|---|
//ClinicalDocument returns nothing | The query looks in no namespace while the document uses urn:hl7-org:v3. | Root element namespace URI and XPath prefix mapping. |
//cda:section reports an expression error | The XPath context does not define the cda prefix. | Namespace resolver or static namespace declarations. |
| Elements are found but attributes are missing | Unprefixed attributes were queried with a namespace prefix. | Use @root, @code or @value unless the attribute itself is prefixed. |
| A query works on one CDA but not another | The expression depends on the source's visible prefix or a namespace is redeclared lower in the tree. | Compare namespace URIs at the exact failing nodes. |
local-name() returns too many nodes | Different vocabularies use the same local element name. | Add namespace-uri() or return to explicit prefix mappings. |
| A text diff reports widespread changes | Prefixes or namespace declaration locations changed even though many expanded names remain equivalent. | Compare namespace-aware structure as well as serialized text. |
| An extension element is ignored | Only the base CDA namespace was configured. | Applicable implementation guide and required extension namespace mappings. |
Troubleshooting
A repeatable namespace investigation
- Parse the original XML without rewriting it. Start from the exact synthetic payload that produces the failure.
- Check for a parser error first. Namespace investigation assumes the document is well-formed XML.
- Inspect the root expanded name. Record both the root local name and namespace URI.
- Inspect namespace declarations in scope. Include declarations on ancestors and any redeclarations near the failing element.
- Identify the XPath version. Confirm whether the processor follows XPath 1.0 or supports a configured default element namespace.
- Bind a query prefix explicitly. Map
cdatourn:hl7-org:v3and update each CDA element name test. - Handle attributes separately. Leave ordinary unprefixed CDA attributes unprefixed in the query.
- Add extension mappings only when needed. Use the exact URI from the document and confirm it is permitted by the applicable profile.
- Use local-name() only to isolate the fault. Replace diagnostic wildcards with namespace-aware expressions before release.
- Retest equivalent serializations. Confirm that default,
cda:and alternative prefixes bound to the same URI produce the same logical selection.
Use the CDA & Clinical XML Viewer to inspect a synthetic document's namespace-aware tree, then use the CDA & Clinical XML Diff Tool to compare structural changes between two namespace variations.
Checklist
What to define in a CDA XML mapping
- The CDA version and implementation guide being processed.
- The expected root local name and namespace URI.
- The XPath version and namespace configuration supported by the runtime.
- A stable query-prefix map for CDA and each permitted extension vocabulary.
- Which attributes are unprefixed and which genuinely belong to another namespace.
- How unknown extension elements are logged, ignored, rejected or preserved.
- Whether comparisons are textual, namespace-aware or schema-aware.
- Tests using equivalent prefixes and intentional namespace mismatches.
- A rule that prevents namespace-agnostic selectors from silently entering production.
- Synthetic regression documents with no patient data or confidential identifiers.
FAQ
Frequently asked CDA namespace questions
Why does //ClinicalDocument return no results?
The CDA element normally belongs to urn:hl7-org:v3, while an unprefixed XPath 1.0 name test looks for an element in no namespace. Bind a query prefix to the CDA namespace and use //cda:ClinicalDocument.
Must the document use the cda prefix?
No. The document can use a default namespace, cda: or another prefix. Namespace-aware processing compares the resolved URI and local name, not the visible prefix text.
Should CDA attributes use the cda prefix in XPath?
Usually not. Ordinary attributes such as root, extension, code and value are normally unprefixed and therefore in no namespace. Query them as @root or @code.
Is local-name() safe for production mappings?
It can be, but it discards the namespace distinction unless paired with namespace-uri(). A prefixed namespace-aware name test is normally clearer and less likely to match extension elements accidentally.
Why does the same XPath work in one product but fail in another?
The products may support different XPath versions or configure the default element namespace differently. Compare the XPath version, static namespace context and explicit prefix mappings rather than only the expression text.
Primary references
CDA, XML namespace and XPath sources
- HL7 CDA Core: ClinicalDocument logical model: the CDA
ClinicalDocumentXML namespace and extension-namespace examples. - W3C Namespaces in XML 1.0, Third Edition: expanded names, namespace declarations, prefix bindings, scope, default namespaces and unprefixed attributes.
- W3C XML Path Language (XPath) Version 1.0: node tests, namespace expansion and the null namespace used for unprefixed QNames.
- W3C XML Path Language (XPath) Version 3.1: default element/type namespaces, expanded QNames and URI-qualified name tests.
Local CDA implementation guides, extension rules, schema packages and processing agreements remain authoritative for the document types accepted by a specific interface.