XML XSLT Basics
XSLT creates a result tree or text output from an input tree. Start with a template matching the document and an explicit output method.
How it works
XSLT is a language for transformations, XPath supplies its selections and expressions, and XSL-FO is a separate formatting vocabulary. A stylesheet is an XML document in the XSLT namespace, not ordinary HTML with a few magic tags. The local Python lab runs XSLT 1.0 through libxslt with file and network access denied. Later Saxon examples deliberately identify XSLT 3.0 features rather than silently mixing processor versions.
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 xslt-first.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 xslt-first.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
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 = "<exchange region=\"west\">\n <packet id=\"p1\" available=\"true\"><name>Marigold</name><quantity>12</quantity><category>flower</category></packet>\n <packet id=\"p2\" available=\"false\"><name>Fenugreek</name><quantity>0</quantity><category>herb</category></packet>\n <packet id=\"p3\" available=\"true\"><name>Basil</name><quantity>7</quantity><category>herb</category></packet>\n</exchange>"
root = parse(source)
style_source = "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n <xsl:output method=\"text\" omit-xml-declaration=\"yes\"/>\n <xsl:template match=\"/\"><xsl:text>Packets: </xsl:text><xsl:value-of select=\"count(exchange/packet)\"/></xsl:template>\n</xsl:stylesheet>"
transform = E.XSLT(parse(style_source), access_control=E.XSLTAccessControl.DENY_ALL)
print(str(transform(root)), end="")
Expected result
Packets: 3
The template matches the document node and counts packet elements under exchange. The output is plain text because xsl:output says so. There is no browser stylesheet dependency or hidden input file.
Check the exact contract in the XML specification, XPath 1.0, the XSD 1.0 primer, lxml validation documentation and Saxon’s processor documentation.