bhylu.com

Advertisements

XML Namespaces

A namespace identifies which vocabulary owns a name. Two organisations can both use a name element without accidentally referring to the same concept.

How it works

A qualified element name consists of a namespace URI and a local name. The visible prefix is a convenient alias, not its identity. Namespace identifiers are compared as strings; declaring one does not make the parser download that address. Choose a stable identifier and supply an explicit namespace mapping when querying. An attribute without a prefix normally has no namespace, even when its element uses a default namespace.

XML to inspect online

namespaces.xml

<s:packet xmlns:s="urn:bhylu:seed:v1" id="p1"><s:name>Marigold</s:name></s:packet>

This checks syntax only. Importing does not run the check. Select Run when ready; DTD and entity declarations are not supported.

Run the code

Use Python 3 with lxml 6.1.1 in a separate virtual environment. The complete listing embeds its own XML and any schema or stylesheet; run python namespaces.py. It does not need Apache or MySQL. The shared restrictive parse function rejects declarations for untrusted input. The trusted DTD/entity and streaming lessons explicitly explain their separate fixed-input boundaries.

Download the complete namespaces.py source. Keep each lab in its own folder. Sources are served as text, not as public executable server scripts. Review the starting setup.

Example

namespaces.py

from lxml import etree as E

class DenyExternal(E.Resolver):
    def resolve(self, url, public_id, context):
        raise ValueError("External resources are disabled")

def parse(text):
    if "<!DOCTYPE" in text.upper() or "<!ENTITY" in text.upper():
        raise ValueError("Declarations are disabled for untrusted XML")
    parser = E.XMLParser(resolve_entities=False, load_dtd=False,
                         no_network=True, huge_tree=False)
    parser.resolvers.add(DenyExternal())
    return E.fromstring(text.encode("utf-8"), parser)

source = "<s:packet xmlns:s=\"urn:bhylu:seed:v1\" id=\"p1\"><s:name>Marigold</s:name></s:packet>"
root = parse(source)
ns = {"seed": "urn:bhylu:seed:v1"}
print(root.tag)
print(root.xpath("string(seed:name)", namespaces=ns))
print(root.get("id"))

Expected result

{urn:bhylu:seed:v1}packet
Marigold
p1

The query uses seed while the source uses s; both resolve to the same namespace URI. The id attribute remains unqualified. This separates the document’s chosen spelling from the query’s stable interpretation.

Check the exact contract in the XML specification, XPath 1.0, the XSD 1.0 primer, lxml validation documentation and Saxon’s processor documentation.