Using XML Namespaces for Extensions Without the Chaos
Let partners add their own data to your XML format safely by giving each extension its own namespace, with a reader that ignores what it does not own.

A format used by more than one party gets requests the owner cannot grant fast enough. A partner wants a shipping code in the order, another wants a campaign id. You cannot add every field to the core format, and telling partners to put things in a free-text note field is how you get parsers for the notes. XML namespaces exist to solve this. They give each party a space for its own names and a way for readers to tell whose element they are looking at.
The shape of an extensible document
The core vocabulary lives in a namespace you own. Extensions live in namespaces owned by whoever defines them. An order that carries a shipping extension from a carrier and an unrelated one from a partner:
<o:order xmlns:o="urn:example:orders:1" xmlns:x="urn:acme:ship" xmlns:y="urn:other:ext">
<o:customer>Ana</o:customer>
<o:total>19.90</o:total>
<x:carrier code="DHL"/>
<y:note>gift</y:note>
</o:order>Each element carries the namespace of whoever defined it. The core elements are o:customer and o:total. x:carrier belongs to the shipping party and y:note to another. Two parties can both define a note and never clash, because {urn:acme:ship}note and {urn:other:ext}note are different names. The prefixes o, x and y are local labels. The identity of an element is the pair of namespace URI and local name (how XML namespaces work explains the mechanism).
A reader that knows its own namespace
A reader written for the core format asks for elements in the core namespace and nothing else. It does not need to know what extensions exist:
from lxml import etree
r = etree.fromstring(open("order.xml", "rb").read())
core = {"o": "urn:example:orders:1"}
print("core children:", [etree.QName(c).localname for c in r.xpath("o:*", namespaces=core)])
print("foreign children:", [c.tag for c in r if etree.QName(c).namespace != core["o"]])
print("total:", r.findtext("o:total", namespaces=core))core children: ['customer', 'total']
foreign children: ['{urn:acme:ship}carrier', '{urn:other:ext}note']
total: 19.90The query o:* selects only elements in the core namespace, so the two extensions are skipped without being named. The second line lists what was skipped, in {namespace}name form, which lets a reader log "ignoring 2 foreign elements" instead of failing. The third line reads a value by its core name. The reader's code does not change when a third party adds a third extension.
The prefix is a convenience and nothing else. Rename o to ord everywhere in the same document, keeping the URI, and the same query on the same mapping still works:
other prefix, same query: 19.90That is why code must never match on the prefix text. A parser that compared the string o:total would break the day a producer chose a different prefix, and valid producers are free to.
Three policies for what you do not know
The format needs a rule for extension elements the reader does not understand. There are three, and the choice depends on what the data is for.
Ignore. Read what you know and drop the rest. This is the default for most data exchange. It keeps old readers working as new extensions appear, and it is the "must ignore" rule from versioning an XML format without breaking readers. The risk is that important information is silently lost.
Preserve. Keep unknown elements and write them back out when you pass the document on. A routing service that reads the core order and forwards it to a carrier should not strip the carrier's extension on the way. This needs a tree model that keeps unknown nodes, not a mapping to a fixed object.
Reject. Refuse documents that contain extensions you did not whitelist. Use this when an unexpected element is a security concern, for example in a signed document where unsigned additions are a classic attack (XML signature wrapping in plain language).
A "must understand" flag is a fourth, optional rule, used by SOAP headers: an element marked mustUnderstand="1" has to be processed or the message rejected. It lets an extender say that ignoring the element would be wrong.
Describing extension points in the schema
If you validate, the schema needs an explicit extension point, as in xs:any with namespace="##other" (versioning an XML format without breaking readers shows the validation output). That allows any element from another namespace at that spot and still rejects typos in the core namespace. The processContents attribute decides what the validator does with them: lax validates an extension if it can find a schema for it and skips it if not, skip never validates and strict demands a schema.
Document the extension points in prose as well as in the schema. For each, say where it may appear, which policy applies, and who registers a new namespace. A registry can be a page of the format specification listing known namespace URIs and owners, or just a convention that a namespace is a URL under a domain you control, so ownership is clear.
Naming extension namespaces
Use a URI you control and will keep stable. A URL under your domain (https://example.com/ns/ship/1) is the common choice because nobody else can mint it, and it does not need to resolve. A URN works too (urn:acme:ship). Put a version in it only if you intend to treat a change as a new vocabulary. An extension namespace that changes with every release forces all readers to update. Do not reuse someone else's namespace for your own elements; that is what causes collisions in the first place.
Related reading
The cluster hub is designing XML documents that age well. Selecting elements in a namespace is covered in selecting elements with a namespace in XPath, and the Python side in handling XML namespaces in ElementTree. The specification is Namespaces in XML 1.0, and the extension patterns follow the W3C TAG finding on extensibility. Test a namespaced path in the XPath tester.
When namespaces for extensions are too much
One producer and one consumer. If you own both ends, add the field to the format and update both. Namespaces are overhead for a closed system.
A flat, short-lived file. A one-off export that will never be extended does not need a namespace architecture.
Your audience cannot use namespaces. Some consumers, such as simple scripts or spreadsheet imports, choke on prefixed names. If the readers are not namespace-aware, extensions in a separate namespace may be harder for them than a documented optional element in the core.
An extension that every reader needs. If an extra field turns out to be required by everyone, promote it into the core in the next version and deprecate the extension. A namespace is a place to start, not a permanent home.


