bhylu.com

Advertisements

XML XSD Wildcards and Substitution

Wildcards allow a contract to carry selected extensions. Keep the namespace and processing policy narrow enough that an extension cannot impersonate a core field.

How it works

xs:any permits elements and xs:anyAttribute permits attributes. processContents="lax" validates an extension when a declaration is available but does not require one; strict requires validation and skip does not attempt it. Substitution groups offer a different approach: explicitly declared compatible global elements can stand in for a head element. These mechanisms trade flexibility against predictability, so document which extensions consumers may ignore and which are required.

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 extension-points.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 extension-points.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

extension-points.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 = "<packet xmlns:m=\"urn:bhylu:meta\"><m:reviewed>yes</m:reviewed></packet>"
root = parse(source)
schema_source = "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" ><xs:element name=\"packet\"><xs:complexType><xs:sequence><xs:any namespace=\"##other\" processContents=\"lax\" minOccurs=\"0\" maxOccurs=\"unbounded\"/></xs:sequence><xs:anyAttribute namespace=\"##other\" processContents=\"lax\"/></xs:complexType></xs:element></xs:schema>"
contract = E.XMLSchema(parse(schema_source))
print(contract.validate(root))


Expected result

True

The extension uses another namespace, so it fits the wildcard. It is accepted without a declaration under lax processing; that does not mean its value yes has been checked against a datatype. The supplemental lab tests a declared substitution group separately.

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