XSLT-Transformer
Wendet ein Stylesheet an, mit WebAssembly als Rückfallebene.
Alles läuft in diesem Tab. Nichts, was Sie einfügen, wird hochgeladen, protokolliert oder irgendwohin gesendet. Öffnen Sie Ihr Netzwerkpanel und prüfen Sie es.
Fügen Sie ein Dokument in den linken Bereich und ein Stylesheet in den rechten ein, und die Transformation läuft in diesem Tab. Das Ergebnis kommt als Text zurück, mit der Engine, die es erzeugt hat, und der benötigten Zeit über der Ausgabe. Beide Eingaben werden zuerst auf Wohlgeformtheit geprüft, sodass eine fehlende spitze Klammer im Stylesheet als solche gemeldet wird und nicht als Übersetzungsfehler.
Man greift danach, wenn ein Stylesheet nicht tut, was man erwartet, und man eine schnelle Schleife will: eine Transformation, die man von jemandem geerbt hat, der weg ist, ein Match-Muster, bei dem man nicht sicher ist, ob es zündet, eine Vorlage, die bei einer Eingabe eine leere Datei erzeugt und bei einer anderen nicht.
Das Werkzeug hat die Form, die es hat, weil XSLT in Browsern nach einem veröffentlichten Zeitplan entfernt wird. Es benutzt den nativen Prozessor, solange es einen gibt, weicht sonst auf einen WebAssembly-Build von libxslt aus und nennt, welche Engine gelaufen ist.
Chrome entfernt XSLT, und die Daten stehen fest
Google hat 2025 eine Absicht zur Abkündigung und Entfernung eingereicht. Die Begründung: Die Verarbeitungsanweisung xml-stylesheet taucht in etwa einem von achttausend Seitenaufrufen auf, während libxslt eine lange Speichersicherheitsbilanz mit sich trägt und im Juni 2025 seinen langjährigen Maintainer verlor. Mozilla und Apple haben beide Zustimmung signalisiert. Der Zeitplan hat Daten:
Firefox und Safari haben keine eigene Entfernung angekündigt, XSLTProcessor funktioniert dort also vorerst weiter. Firefox benutzt seinen eigenen Prozessor TransforMiiX statt libxslt, und darum verhalten sich eine Handvoll Stylesheets zwischen Browsern schon heute unterschiedlich.
Diese Seite prüft auf einen funktionierenden nativen Prozessor, indem sie eine einzeilige Transformation ausführt, statt nachzusehen, ob der Konstruktor existiert: Einer, der existiert und still scheitert, ist schlimmer als einer, der fehlt. Scheitert die Probe, lädt sie einen WebAssembly-Build von libxslt – das Polyfill, auf das Chromes eigene Migrationshinweise zeigen.
- Chrome 143, 2. Dezember 2025: offizielle Abkündigung, Warnungen in Konsole und Lighthouse.
- Chrome 145, Dezember 2025: XSLT in Canary, Dev und Beta standardmäßig aus.
- Chrome 146, 10. März 2026: Notausgang per Unternehmensrichtlinie.
- Chrome 152, 25. August 2026: Origin Trial beginnt.
- Chrome 158, 17. November 2026: XSLT funktioniert im Stable nicht mehr, außer unter dem Trial oder der Richtlinie.
- Chrome 176, 17. August 2027: Beide Notausgänge laufen ab. XSLTProcessor und die Verarbeitungsanweisung xml-stylesheet sind weg. Die Form type="text/css" derselben Anweisung ist nicht betroffen.
Vorlagen sind Regeln, kein Skript
Ein Stylesheet ist eine Menge von Vorlagenregeln, jede mit einem Match-Muster. Der Prozessor beginnt an der Dokumentwurzel, sucht die am besten passende Vorlage, führt sie aus, und wo diese Vorlage xsl:apply-templates sagt, wiederholt er das Verfahren für die ausgewählten Kinder. Nichts läuft in der Reihenfolge, in der Sie es geschrieben haben.
xsl:apply-templates gibt die Kontrolle an die Regelmenge zurück: Jeder ausgewählte Knoten findet seine eigene Vorlage, ungleichartige Kinder bekommen also eigene Regeln statt einer Kette von xsl:choose-Zweigen. xsl:for-each iteriert direkt und wendet einen Rumpf auf jeden ausgewählten Knoten an, was sich für eine flache Liste von Zeilen besser liest. Beide ändern den Kontextknoten, und daher stammen die meisten Meldungen über einen plötzlich falschen select-Ausdruck.
XSLT hat außerdem eingebaute Regeln, die Sie nicht geschrieben haben: Die Vorgabe für Elemente wendet Vorlagen auf ihre Kinder an, und die Vorgabe für Textknoten reicht den Text durch. Dieses Paar ist der Grund, warum ein Stylesheet, dessen Muster auf nichts passen, trotzdem eine Seite ineinanderlaufenden Textes erzeugt.
Wenn die Transformation nichts erzeugt
Ein leeres Ergebnis ist das am zweithäufigsten gemeldete XSLT-Problem nach ineinanderlaufendem Text, und es hat eine beherrschende Ursache: Namensräume. Ein Stylesheet, das für XML ohne Namensraum geschrieben wurde, passt nicht auf Elemente, die in einem Namensraum liegen, auch wenn die Namen in beiden Dateien identisch aussehen. Trägt Ihre Quellwurzel xmlns="http://example.com/orders", dann fragt <xsl:template match="order"> nach {}order, während das Dokument {http://example.com/orders}order enthält. Keine Vorlage zündet, und XSLT hält das nicht für einen Fehler.
Deklarieren Sie den Namensraum im Stylesheet unter einem Präfix Ihrer Wahl und benutzen Sie es in jedem match und jedem select. XSLT 1.0 teilt die Einschränkung von XPath 1.0, das Präfix ist also nicht optional, und xpath-default-namespace hilft nicht, weil es zu XSLT 2.0 gehört und Browser es ignorieren.
Ein Stylesheet, das version="2.0" oder "3.0" deklariert, wird vor dem Lauf markiert, denn beide Engines sind 1.0, und spätere Konstrukte können still scheitern, was eine subtil falsche statt einer fehlenden Ausgabe ergibt. Und liegt die Stylesheet-Wurzel nicht in http://www.w3.org/1999/XSL/Transform, funktioniert überhaupt nichts: Diese URI ist für alle drei XSLT-Versionen identisch, und eine verschriebene übersetzt zu einem Stylesheet ohne jede Regel darin.
Was keine XSLT-Engine im Browser kann
Nur XSLT 1.0. Kein Browser hat je 2.0 oder 3.0 ausgeliefert, und libxslt implementiert sie ebenfalls nicht, der WebAssembly-Rückfall hebt die Decke also nicht an. Damit fallen xsl:function, xsl:for-each-group (Gruppierung bleibt die Muenchian-Technik mit key()), xsl:analyze-string und xsl:result-document weg. Saxon-JS ist der einzige Weg zu 2.0 oder 3.0 im Browser.
Externe Ressourcen sind die andere harte Grenze. document(), xsl:include und xsl:import entfernter URLs unterliegen der Same-Origin-Policy, ein Stylesheet, das eine Nachschlagetabelle über HTTP hereinzieht, scheitert also. Hier holt nichts etwas für Sie, und es gibt keinen Proxy. Ein paar engine-spezifische Lücken erwischen ebenfalls viele:
- disable-output-escaping ist in Firefox nicht implementiert: TransforMiiX erzeugt einen Baum statt einer serialisierten Zeichenkette, und die Spezifikation erlaubt dem Prozessor, sich zu erholen, indem er das Attribut als no behandelt. Nehmen Sie stattdessen eine numerische Zeichenreferenz wie  .
- xsl:namespace-alias wird in Firefox nicht unterstützt, ebenso wenig die XPath-Achse namespace::.
- xsl:output ist weitgehend beratend, wenn der Prozessor aus einem Skript gesteuert wird, denn das Ergebnis ist ein DOM: indent, encoding und die Doctype-Eigenschaften haben kaum Wirkung. Diese Seite liest daraus method, um zwischen Text und serialisiertem Markup zu entscheiden.
- Die EXSLT-Abdeckung unterscheidet sich. libxslt, von Chrome, Safari und dem WebAssembly-Rückfall genutzt, implementiert das meiste. Firefox implementiert eine Teilmenge.
- transformToDocument und transformToFragment geben im Fehlerfall null zurück, statt zu werfen, und der wirkliche Grund erscheint nur in der Konsole.
Das im Code tun
Ein Stylesheet ist ausführbare Eingabe. Jede Engine unten kann über document(), xsl:import oder eine Erweiterungsfunktion Dateien lesen, Netzverbindungen öffnen oder auf die Platte schreiben, also schaltet jedes Beispiel das ab. Behandeln Sie ein Stylesheet, das Sie nicht geschrieben haben, wie ein Skript, das Sie nicht geschrieben haben.
// Native first, WebAssembly fallback. A probe, not a feature check:
// after Chrome 158 the constructor is gone, but a constructor that
// exists and fails is the case that actually misleads you.
function nativeXsltWorks() {
if (typeof XSLTProcessor === 'undefined') return false;
try {
const p = new DOMParser();
const xsl = p.parseFromString(
'<xsl:stylesheet version="1.0" ' +
'xmlns:xsl="http://www.w3.org/1999/XSL/Transform">' +
'<xsl:template match="/"><ok/></xsl:template></xsl:stylesheet>',
'application/xml');
const proc = new XSLTProcessor();
proc.importStylesheet(xsl);
const out = proc.transformToDocument(
p.parseFromString('<a/>', 'application/xml'));
return out?.documentElement?.nodeName === 'ok';
} catch {
return false;
}
}
async function transform(xmlText, xslText) {
// xslt-polyfill is libxslt compiled to WebAssembly. Importing it
// installs a replacement XSLTProcessor on globalThis.
if (!nativeXsltWorks()) await import('xslt-polyfill');
const p = new DOMParser();
const xml = p.parseFromString(xmlText, 'application/xml');
const xsl = p.parseFromString(xslText, 'application/xml');
if (xml.querySelector('parsererror') || xsl.querySelector('parsererror')) {
throw new Error('one of the two inputs is not well-formed');
}
const proc = new XSLTProcessor();
proc.importStylesheet(xsl);
proc.setParameter(null, 'lang', 'en'); // (namespaceURI, name, value)
// Returns null rather than throwing. Do not skip this check.
const out = proc.transformToDocument(xml);
if (!out) throw new Error('no template matched the document root');
return new XMLSerializer().serializeToString(out);
}from lxml import etree
# lxml wraps libxslt, the same engine Chrome and Safari use.
parser = etree.XMLParser(resolve_entities=False, no_network=True, load_dtd=False)
xml = etree.fromstring(xml_bytes, parser)
xsl = etree.fromstring(xsl_bytes, parser)
# DENY_ALL blocks document(), xsl:include, xsl:import and every EXSLT
# file or network operation. Without it a stylesheet is a file-read
# primitive that runs whatever its author wrote.
transform = etree.XSLT(xsl, access_control=etree.XSLTAccessControl.DENY_ALL)
try:
result = transform(xml, lang="'en'") # params are XPath: quote strings twice
except etree.XSLTApplyError as e:
raise SystemExit(f'transform failed: {e}')
# xsl:message output and warnings land here, not in the result.
for entry in transform.error_log:
print(f'{entry.line}: {entry.message}')
print(str(result)) # honours xsl:output method, encoding and indentimport javax.xml.XMLConstants;
import javax.xml.transform.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
TransformerFactory factory = TransformerFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
// An empty string means "no protocol is permitted", which blocks
// document(), xsl:import and xsl:include of anything outside the
// stylesheet you handed in. Neither is restricted by default.
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
// Without a listener, compilation warnings go to stderr and are lost.
factory.setErrorListener(new ErrorListener() {
public void warning(TransformerException e) {
System.err.println("warning: " + e.getMessageAndLocation());
}
public void error(TransformerException e) throws TransformerException {
throw e;
}
public void fatalError(TransformerException e) throws TransformerException {
throw e;
}
});
// Templates is compiled once and is thread-safe; Transformer is not.
Templates templates = factory.newTemplates(new StreamSource(stylesheetStream));
Transformer transformer = templates.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setParameter("lang", "en");
transformer.transform(new StreamSource(xmlStream), new StreamResult(System.out));
// The JDK's built-in processor is Xalan, XSLT 1.0. For 2.0 or 3.0 put
// Saxon-HE on the classpath; it registers itself as the factory.using System.Xml;
using System.Xml.Xsl;
var readerSettings = new XmlReaderSettings
{
DtdProcessing = DtdProcessing.Prohibit,
XmlResolver = null,
};
var transform = new XslCompiledTransform();
// XsltSettings.Default disables both the document() function and
// embedded <msxsl:script>. XsltSettings.TrustedXslt enables both and
// should never be used on a stylesheet from outside your codebase.
// The null resolver blocks xsl:import and xsl:include as well.
using (var xslReader = XmlReader.Create(stylesheetPath, readerSettings))
{
transform.Load(xslReader, XsltSettings.Default, null);
}
var args = new XsltArgumentList();
args.AddParam("lang", "", "en");
using var xmlReader = XmlReader.Create(inputPath, readerSettings);
using var writer = XmlWriter.Create(Console.Out, transform.OutputSettings);
transform.Transform(xmlReader, args, writer);
// XslCompiledTransform is XSLT 1.0. Microsoft never shipped 2.0 for
// .NET; Saxonica's Saxon-HE has a .NET build if you need it.<?php
// Requires ext-xsl, which wraps libxslt.
$xml = new DOMDocument();
$xml->loadXML($xmlSource, LIBXML_NONET);
$xsl = new DOMDocument();
$xsl->loadXML($xslSource, LIBXML_NONET);
$proc = new XSLTProcessor();
// Block every filesystem and network operation a stylesheet can reach
// for. Only the write prefs are blocked by default.
$proc->setSecurityPrefs(
XSL_SECPREF_READ_FILE
| XSL_SECPREF_WRITE_FILE
| XSL_SECPREF_CREATE_DIRECTORY
| XSL_SECPREF_READ_NETWORK
| XSL_SECPREF_WRITE_NETWORK
);
$proc->importStylesheet($xsl);
$proc->setParameter('', 'lang', 'en');
$result = $proc->transformToXml($xml);
if ($result === false) {
fwrite(STDERR, "transformation failed\n");
exit(1);
}
echo $result;# xsltproc ships with libxslt: the same engine Chrome and Safari use,
# and the same one this page falls back to in WebAssembly.
# --nonet stops it fetching anything the stylesheet references.
xsltproc --nonet style.xsl doc.xml > out.html
# --stringparam passes a string. --param takes an XPath expression, so
# a literal string needs its own quotes inside the shell quotes.
xsltproc --nonet --stringparam lang en style.xsl doc.xml
xsltproc --nonet --param year "'2026'" style.xsl doc.xml
# xsl:message output goes to stderr, so separate it before piping.
xsltproc --nonet style.xsl doc.xml 2> messages.log | xmllint --format -
# XSLT 2.0 and 3.0 need Saxon. libxslt will never implement them.
java -cp saxon-he.jar net.sf.saxon.Transform \
-s:doc.xml -xsl:style.xsl -o:out.html lang=enDie Parametersyntax unterscheidet sich in jedem davon, und nicht bloß kosmetisch: In lxml, in xsltproc --param und in Saxon ist ein Parameterwert ein XPath-Ausdruck, die Zeichenkette en muss also "'en'" geschrieben werden, sonst wird sie als Pfadschritt gelesen, der Elemente namens en auswählt. setParameter in JavaScript und XsltArgumentList in .NET nehmen einfache Zeichenketten.
Häufige Fragen
Hört dieses Werkzeug auf zu funktionieren, wenn Chrome XSLT entfernt?
Nein. Chrome 158 lässt XSLT am 17. November 2026 im Stable nicht mehr laufen, und Chrome 176 entfernt am 17. August 2027 XSLTProcessor und die Verarbeitungsanweisung xml-stylesheet ganz. Ein Werkzeug, das allein auf dem nativen Prozessor aufbaut, ginge an diesen Daten kaputt.
Diese Seite führt zuerst eine einzeilige Probetransformation aus. Solange Ihr Browser eine funktionierende native Engine hat, wird diese benutzt; scheitert die Probe, wird stattdessen ein WebAssembly-Build von libxslt geladen, und das Panel nennt die gelaufene Engine. Der Rückfall wird nur bei Bedarf geholt, Nutzerinnen und Nutzer von Firefox und Safari laden ihn also nie herunter.
Kann ich hier XSLT 2.0 oder 3.0 ausführen?
Nein, und kein Browser kann das. Chrome und Safari benutzen libxslt, Firefox sein eigenes TransforMiiX, und alle drei implementieren 1.0. Der WebAssembly-Rückfall ist ebenfalls libxslt, er ändert die Decke also nicht.
Deklariert Ihr Stylesheet version="2.0" oder "3.0", warnt diese Seite vor dem Lauf statt danach, denn ein 1.0-Prozessor weist ein 2.0-Stylesheet nicht rundheraus zurück: Er führt aus, was er erkennt, und behandelt den Rest still falsch. Saxon ist die praktische Antwort, Saxon-JS im Browser und Saxon-HE auf der JVM oder .NET.
Warum erzeugt mein Stylesheet ein leeres Ergebnis?
Fast immer eine Namensraum-Diskrepanz. Deklariert Ihre Quelle an ihrer Wurzel einen Standardnamensraum wie xmlns="http://example.com/orders", gehört jedes präfixlose Element darin zu diesem Namensraum. Eine Vorlage, geschrieben als <xsl:template match="order">, fragt nach einem order-Element in keinem Namensraum, es zündet also keine Regel und die Ausgabe ist leer. XSLT behandelt das nicht als Fehler.
Deklarieren Sie den Namensraum im Stylesheet mit einem Präfix, etwa xmlns:o="http://example.com/orders", und schreiben Sie dann überall match="o:order" und select="o:line". Zwei weitere Ursachen zum Ausschließen: eine Stylesheet-Wurzel, die nicht in http://www.w3.org/1999/XSL/Transform liegt, und keine Vorlage, die auf die Dokumentwurzel passt.
Werden mein XML und mein Stylesheet hochgeladen?
Weder noch. Beide Bereiche bleiben in diesem Tab: Die Wohlgeformtheitsprüfung läuft in einem Web Worker, und die Transformation läuft entweder auf dem eingebauten Prozessor Ihres Browsers oder auf einem in die Seite geladenen WebAssembly-Modul. Es gibt hier keinen Server, der sie entgegennehmen könnte.
Die Frage ist bei XSLT schärfer als bei den meisten Werkzeugen, denn das Stylesheet ist oft die wertvolle Hälfte: Eine Transformation, die das Schema eines Partners auf Ihr internes abbildet, kodiert Geschäftsregeln, Feldnamen und manchmal Endpunkte. Eine Folge des lokalen Laufs ist, dass die Ausgabe als Text gezeigt und nie als Markup in die Seite eingefügt wird, denn ein Stylesheet kann script-Elemente erzeugen.
Warum enthält meine Ausgabe den ganzen Text meines Dokuments ohne Markup?
Sie sind auf die eingebauten Vorlagenregeln von XSLT gestoßen. Die Spezifikation definiert Vorgaben, die es gibt, ob Sie sie schreiben oder nicht: Die Regel für Elemente wendet Vorlagen auf ihre Kinder an, und die Regel für Textknoten reicht den Text direkt durch.
Wenn also keine Ihrer eigenen Vorlagen passt, hört der Prozessor nicht auf. Er läuft mit diesen Vorgaben durch den ganzen Baum und gibt jeden gefundenen Textknoten aus, samt der Einrückung der Quelle. Die Ursache ist meist ein Namensraum. Die schnelle Diagnose ist, <xsl:template match="text()"/> hinzuzufügen, was die Textvorgabe unterdrückt: Wird die Ausgabe leer, hat keine Ihrer Regeln gezündet.
Was ist der Unterschied zwischen apply-templates und for-each?
xsl:for-each iteriert direkt. Es wählt eine Knotenliste und führt seinen Rumpf einmal je Knoten in Dokumentreihenfolge aus, und dieser Rumpf ist fest, jeder Knoten wird also gleich behandelt. Es liest sich wie eine Schleife und passt zu einer flachen, gleichartigen Liste, etwa jedes row-Element in eine Tabellenzeile zu verwandeln.
xsl:apply-templates gibt die Kontrolle an die Regelmenge zurück und lässt jeden ausgewählten Knoten die am besten passende Vorlage finden, verschiedene Elementtypen werden also ohne Bedingungen verschieden behandelt. Es nimmt außerdem ein mode-Attribut, derselbe Knoten kann also zweimal unter verschiedenen Regeln verarbeitet werden, einmal für ein Inhaltsverzeichnis und einmal für den Fließtext. for-each hat dafür keine Entsprechung.
Kann das Stylesheet mit document() oder xsl:import eine andere Datei laden?
Hier nicht. document(), xsl:include und xsl:import lösen alle eine URL auf, und im Browser unterliegen diese Ladevorgänge der Same-Origin-Policy, alles Entfernte ist also blockiert. Diese Seite hat außerdem bewusst keinen Abruf-Proxy: Zu proxyen hieße, dass das Zieldokument und die URL selbst über einen Server laufen, den diese Seite kontrolliert.
Binden Sie das zweite Dokument in Ihre Quelle ein und wählen Sie es mit gewöhnlichem XPath aus, verschieben Sie die Nachschlagetabelle in einen Stylesheet-Parameter, oder führen Sie die Transformation lokal mit xsltproc aus, wo document() gegen Ihr Dateisystem auflöst.