XML Namespaces Explained: Prefixes, Default Namespaces and Common Errors
Namespaces are the part of XML that most people learn by copying an xmlns line from a working file and hoping. That works until a validator says "unbound prefix", an XPath query returns an empty list, or an attribute that looks namespaced turns out not to be. This guide explains what a namespace declaration actually does to the names in a document, where the rules are counter-intuitive, and how SOAP, Atom, SVG and XML Schema use them in practice.
In short
An XML namespace declaration (xmlns or xmlns:prefix) gives every element and attribute name a second part — a URI — so vocabularies mixed in one document cannot collide. The default namespace applies only to unprefixed elements, never to attributes, which is the rule that breaks the most validators and XPath queries. Namespace URIs are compared as opaque strings and never fetched over the network.
Why namespaces exist
XML lets anyone invent element names, so two vocabularies mixed in one document will collide sooner or later. An order document that embeds a Dublin Core title next to its own title has no way to tell a processor which is which; a parser sees two elements with the same name and either merges them into one list or picks the wrong one. Namespaces, defined in a separate W3C recommendation (Namespaces in XML, 1999), solve this by giving every element and attribute name a second part: a URI that identifies the vocabulary it belongs to.
A name in a namespaced document is therefore a pair, the namespace URI plus the local name, usually written in Clark notation as {uri}local. Two elements are the same element only if both parts match. The prefix you see in the file is not part of the name; it is a local shorthand that the parser expands and then forgets. That one fact explains most of the surprises that follow. In the example, the two title elements have different expanded names and can coexist without ambiguity.
<order xmlns="https://example.com/ns/orders"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<title>Order #1042</title> <!-- {https://example.com/ns/orders}title -->
<dc:title>Widget catalogue</dc:title> <!-- {http://purl.org/dc/elements/1.1/}title -->
</order>xmlns and xmlns:prefix: binding and scope
xmlns:dc="..." binds the prefix dc to a URI for the element it appears on and all of that element's descendants. xmlns="..." with no prefix sets the default namespace: every unprefixed element in scope belongs to it. Both look like attributes, but namespace-aware parsers do not report them as attributes; they are declarations the parser consumes while building names. A declaration can appear on any element, not only the root, and it can be overridden: a nested element may rebind the same prefix to a different URI, and xmlns="" resets the default namespace so that unprefixed descendants have no namespace at all.
Prefix names are arbitrary. soap:Envelope bound to the SOAP 1.2 URI and env:Envelope bound to the same URI are identical to a namespace-aware parser, and a schema validator accepts either. Only two prefixes are reserved: xml is permanently bound to http://www.w3.org/XML/1998/namespace, and xmlns to the declaration namespace itself. Binding either to anything else is a fatal error; expat, the parser behind Python's ElementTree, reports "reserved prefix (xml) must not be undeclared or bound to another namespace name".
The scoping example below is worth tracing element by element. b inherits the outer default; c switches the default, and d inherits the switched one; e undeclares the default, so e and f are in no namespace. Python's ElementTree, which is namespace-aware, reports their tags as {urn:example:outer}b, {urn:example:inner}d and plain f.
| Form | Effect |
|---|---|
| xmlns="uri" | Sets the default namespace for this element and its unprefixed descendants |
| xmlns:prefix="uri" | Binds prefix to a URI for this element and its descendants |
| xmlns="" | Undeclares the default namespace for descendants (no namespace) |
| xml: / xmlns: prefixes | Reserved — xml is bound to the XML namespace, xmlns to the declaration namespace itself |
<a xmlns="urn:example:outer">
<b> <!-- {urn:example:outer}b -->
<c xmlns="urn:example:inner"> <!-- {urn:example:inner}c -->
<d/> <!-- {urn:example:inner}d, inherited -->
</c>
<e xmlns=""> <!-- no namespace -->
<f/> <!-- no namespace, inherited from e -->
</e>
</b>
</a>Attributes are not in the default namespace
This is the rule that trips up almost everyone: the default namespace applies to elements only. An unprefixed attribute has no namespace, even when it sits on an element that is in the default namespace. In an Atom feed, <title type="text"> is the element {http://www.w3.org/2005/Atom}title, but its attribute is plain type; ElementTree reports the attribute key as 'type', not '{http://www.w3.org/2005/Atom}type', and a schema describes it as an unqualified attribute.
Only a prefixed attribute is namespaced. xsi:schemaLocation and xlink:href belong to the XSI and XLink namespaces because of their prefixes. Two consequences follow. An element may carry a:id and b:id at the same time, since they are different names, but never two unprefixed id attributes.
And a schema that declares attributes as qualified (attributeFormDefault="qualified") requires the prefix on every instance; leaving it off produces a validator complaint that the attribute is not allowed, even though it is visibly there. The example below has one element in urn:d, one attribute in urn:p and one attribute in no namespace, despite the default declaration.
<r xmlns="urn:d" xmlns:p="urn:p" p:k="v" k="w"/>
<!-- element : {urn:d}r
attribute : {urn:p}k = "v" (prefixed, so namespaced)
attribute : k = "w" (unprefixed: NO namespace, despite xmlns="urn:d") -->Namespace URIs are identifiers, not links
A namespace URI does not have to resolve to anything. http://www.w3.org/2005/Atom happens to be a real page; http://schemas.xmlsoap.org/soap/envelope/ used to be; urn:oasis:names:tc:SAML:2.0:assertion was never meant to be fetched. Parsers compare the string character by character and never make a network request. That is why http://example.com/ns and http://example.com/ns/ are two different namespaces, why HTTP:// differs from http://, and why a stray space inside the quotes silently breaks schema validation with an "element not expected" error rather than anything mentioning namespaces.
The practical rules: copy namespace URIs exactly from the specification that owns the vocabulary, treat them as opaque, and never "fix" one that looks like a dead link. When designing your own, use something you control, such as https://yourcompany.com/ns/orders/2026, so it cannot collide with anyone else's, and include a version so that a breaking change can get a new namespace instead of silently invalidating old documents. A firewall that blocks outbound HTTP has nothing to do with namespaces; if validation fails offline, the cause is a schema location that cannot be fetched, not the namespace URI.
SOAP envelopes and feed extensions
A SOAP message is the canonical mixed document. The envelope, header and body belong to the SOAP namespace, while the payload inside the body belongs to whatever service defines it. In the example the soap prefix is declared on the root and the m prefix on the payload element itself, a declaration scoped as narrowly as its use. A SOAP client that knows the envelope namespace can route any payload without understanding it; a schema for the payload can validate it without knowing about SOAP. Pretty-printing a payload like this before you eyeball it is exactly what the XML Formatter does.
Feeds use the other common arrangement: a default namespace for the format's own elements and prefixes for extensions. Atom declares xmlns="http://www.w3.org/2005/Atom" on the feed and adds media:thumbnail from Media RSS or dc:creator from Dublin Core as needed. RSS 2.0 itself has no namespace, so its elements are in none, and extensions such as content:encoded and itunes:author are the only namespaced parts of a podcast feed. Either way a reader that does not know an extension can skip it by namespace and still render the rest of the feed.
<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Header/>
<soap:Body>
<m:GetPrice xmlns:m="https://example.com/prices">
<m:Item>Apples</m:Item>
</m:GetPrice>
</soap:Body>
</soap:Envelope>SVG inside HTML and targetNamespace in XSD
HTML5 is not XML, and its parser assigns the SVG and MathML namespaces automatically when it meets <svg> or <math>, so an inline <svg viewBox="0 0 40 40"> in an .html page needs no xmlns at all. The declaration is required in a standalone .svg file, in XHTML served as application/xhtml+xml, and whenever elements are created from script: document.createElement("svg") produces an HTML element that never renders, while document.createElementNS("http://www.w3.org/2000/svg", "svg") produces the real thing. The same applies to attributes such as xlink:href, which need setAttributeNS with the XLink URI.
In XML Schema, targetNamespace states which namespace the schema defines. The schema document uses the xs prefix for the schema language itself and usually declares the target namespace as its own default so that type references such as type="Order" resolve to the types it defines. elementFormDefault="qualified" makes locally declared child elements namespaced too. Leave it at the default, unqualified, and instance documents must write those children without a namespace, which means a document written with a single default declaration on the root will fail validation with "element 'id' is not expected" even though it looks perfectly consistent.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="https://example.com/ns/orders"
xmlns="https://example.com/ns/orders"
elementFormDefault="qualified">
<xs:element name="order" type="Order"/>
<xs:complexType name="Order">
<xs:sequence>
<xs:element name="id" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>XPath returns nothing, and "unbound prefix"
The most common namespace bug is not a parse error at all: //Body returns nothing on a SOAP message. XPath 1.0 has no default namespace, so an unprefixed name in a query matches only elements that are in no namespace. Every element of a SOAP envelope is namespaced, so the query is valid XPath that simply matches nothing. The fix is to register a prefix with the query engine and use it: //s:Body. The prefix you register does not have to match the one in the document; only the URI matters, so s and soap are interchangeable as long as both map to the SOAP 1.2 URI.
Every XPath API takes that prefix map somewhere: lxml's xpath(expr, namespaces=...), ElementTree's find(path, namespaces), the browser's document.evaluate(expr, doc, resolver, ...) where the resolver is a function from prefix to URI, and Java's XPath.setNamespaceContext. The escape hatch *[local-name()='Body'] matches by local name regardless of namespace; it is fine for one-off scripts, but it will happily match a Body element from an unrelated vocabulary.
The second failure is the message "unbound prefix" (expat, and therefore Python), "Namespace prefix foo on bar is not defined" (libxml2, which backs xmllint, PHP and browsers' DOMParser), or "The prefix 'foo' for element 'foo:bar' is not bound" (Java). They all mean the same thing: an element or attribute uses foo: but no ancestor declares xmlns:foo. It typically appears after copying a fragment out of a larger document, because the declaration lived on an ancestor that did not come along; add the declaration to the fragment's root. Note that this check belongs to namespace processing, not to well-formedness. A validator that only checks well-formedness, such as fast-xml-parser's XMLValidator, accepts <root><foo:bar/></root> without complaint, and that library's parser keeps foo:bar as a literal tag name rather than expanding it.
from lxml import etree
doc = etree.fromstring(b"""<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Body>
<m:GetPrice xmlns:m="https://example.com/prices"><m:Item>Apples</m:Item></m:GetPrice>
</soap:Body>
</soap:Envelope>""")
print(doc.xpath("//Body")) # [] (no element named Body in *no* namespace)
ns = {"s": "http://www.w3.org/2003/05/soap-envelope", "p": "https://example.com/prices"}
print(doc.xpath("//s:Body/p:GetPrice/p:Item/text()", namespaces=ns)) # ['Apples']
print(doc.xpath("//*[local-name()='Item']/text()")) # ['Apples'], prefix-agnosticSources
Frequently asked questions
Do I need a namespace for my own XML?
Not for a private format that only your code reads. The moment the document is validated against an XSD, embedded in another document (a SOAP body, a feed, a configuration wrapper) or exchanged with another organisation, a namespace is the only thing that makes your element names unambiguous, so it is cheap insurance to declare one from the start.
Why does my XML to JSON converter produce keys like soap:Body?
Most lightweight converters, fast-xml-parser included, are not namespace-aware: they treat the prefix as part of the tag name and xmlns declarations as ordinary attributes. Some offer an option to strip prefixes (removeNSPrefix in fast-xml-parser), which gives you Body but also erases the distinction between two vocabularies that share a local name. A namespace-aware parser would instead expose the expanded {uri}local name.
What is the difference between xmlns and xsi:schemaLocation?
xmlns declares which namespace names belong to; it is part of the document's meaning. xsi:schemaLocation is an optional hint listing pairs of namespace URI and XSD file location that a validator may use to find schemas. Validators are free to ignore it, and many production setups do, resolving schemas from a local catalogue instead. Removing schemaLocation never changes what an element means; removing xmlns does.
Why does getElementsByTagName("title") miss elements in my parsed XML?
In a DOM built by DOMParser with the XML content type, getElementsByTagName matches the qualified name exactly as written, so it depends on the prefix the author happened to use and does not see a default-namespaced title if you ask for dc:title or vice versa. Use getElementsByTagNameNS(namespaceURI, "title"), which matches on the expanded name and works regardless of prefix.
Paste a namespaced document into the XML Validator to check well-formedness with line and column positions before it reaches a strict parser.
Validate your XMLKeep working
Related tools
XML Validator
Strict well-formedness checking with the exact line and column of every violation — fix XML errors in seconds.
XML Formatter
Re-indent any XML document into clean, readable markup — comments and CDATA preserved, validation built in.
SOAP Formatter
Turn captured SOAP envelopes into readable XML — Header and Body cleanly indented, namespaces intact.
XML to JSON
Parse any XML document into clean JSON — repeated elements become arrays, attributes preserved, text nodes kept.
WSDL Formatter
Make machine-generated WSDL readable — messages, portTypes, bindings and services cleanly indented and explorable.
RSS to JSON
Turn any RSS feed into clean JSON — channel metadata and an items array, ready for your app or script.
XML Escape
The five XML entities, handled both directions — make any text safe inside elements and attributes.