Den Stylesheet-Fluss durch sein Eingabedokument mitverfolgen

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

Sie möchten mitverfolgen (engl. trace), wie Ihr Stylesheet durch ein XML-Dokument navigiert.

Lösung

XSLT 1.0

Als Erstes sollten Sie überprüfen, welche Trace-Optionen in Ihrem XSLT-Prozessor verfügbar sind. Saxon kennt die Option -t, die Zeitangaben zu verschiedensten Verarbeitungschritten macht, sowie die Option -T, die eine Ausgabe von Trace-Informationen bewirkt. Xalan verfügt über -TT, das die Templates mitverfolgt, sobald sie aufgerufen werden, -TG, mit dem alle Erzeugungsereignisse mitverfolgt werden, -TS für alle Auswahlereignisse und -TTC zum Mitverfolgen von aufgerufenen Template-Kindern.

Falls Ihr Prozessor keine Trace-Ausgabe unterstützt oder wenn Sie ein höheres Maß an Einfluss über die Ausgabe benötigen, können Sie eventuell eine Lösung in Betracht ziehen, die auf xsl:message basiert. Mit xsl:message kann man leicht eine Debug-Ausgabe generieren, mit der sich der Kontrollfluss durch das Stylesheet mitverfolgen lässt. Es ist auch hilfreich, den Fluss des Stylesheets durch das Dokument mitzuverfolgen. Hier ist ein Hilfsmittel, das Sie zu diesem Zweck in jedes beliebige Stylesheet importieren können:

<!-- xtrace.xslt -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dbg="http://www.ora.com/XSLTCookbook/ns/debug">
  <xsl:param name="debugOn" select="false( )"/>
  <xsl:template match="node( )" mode="dbg:trace" name="dbg:xtrace">
    <xsl:param name="tag" select=" 'xtrace' "/>
    <xsl:if test="$debugOn">
      <xsl:message>
        <xsl:value-of select="$tag"/>: <xsl:call-template name="dbg:expand-path"/>
      </xsl:message>
    </xsl:if>
  </xsl:template>
  <!-- Erweitere den xpath auf den aktuellen Knoten -->
  <xsl:template name="dbg:expand-path">
    <xsl:apply-templates select="." mode="dbg:expand-path"/>
  </xsl:template>
  <!-- Wurzel -->
  <xsl:template match="/" mode="dbg:expand-path">
    <xsl:text>/</xsl:text>
  </xsl:template>
  <!-- Oberster Knoten -->
  <xsl:template match="/*" mode="dbg:expand-path">
    <xsl:text>/</xsl:text><xsl:value-of select="name( )"/>
  </xsl:template>
  <!-- Knoten mit Elternknoten -->
  <xsl:template match="*/*" mode="dbg:expand-path">
    <xsl:apply-templates select=".." mode="dbg:expand-path"/>/<xsl:value-ofselect="name( )"/>[<xsl:number/>]<xsl:text/>
  </xsl:template>
  <!-- Attributknoten -->
  <xsl:template match="@*" mode="dbg:expand-path">
    <xsl:apply-templates select=".." mode="dbg:expand-path"/>/@<xsl:value-of select="name( )"/>
  </xsl:template>
  <!-- Textknoten (der Übersicht wegen normalisiert) -->
  <xsl:template match="text( )" mode="dbg:expand-path">normalized-text(<xsl:value-of select="normalize-space(.)"/>)</xsl:template>
</xsl:stylesheet>

Wenn Sie in Ihrem Stylesheet Aufrufe von dbg:xtrace verwenden, so erzeugen Sie dabei eine Meldung, die den Pfad zum aktuellen Knoten enthält. Der folgende Code zum Beispiel beinhaltet ein Identitäts-Stylesheet mit einem xtrace:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dbg="http://www.ora.com/XSLTCookbook/ns/debug">
  <xsl:include href="xtrace.xslt"/>
  <xsl:template match="/ | node( ) | @* | comment( ) | processing-instruction( )">
    <xsl:call-template name="dbg:trace"/>
    <xsl:copy>
      <xsl:apply-templates select="@* | node( )"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Dann erhalten Sie mit dieser Testeingabe:

<test foo="1">
  <someElement n="1"/>
  <someElement n="2">
    <someChild>someValue</someChild>
  </someElement>
  <someElement n="3">
    <someChild>someOtherValue</someChild>
  </someElement>
  <someElement n="4">
    <someChild>someValue</someChild>
  </someElement>
</test>

die folgende Debugging-Ausgabe:

xtrace: /
xtrace: /test
xtrace: /test/@foo
xtrace: normalized-text( )
xtrace: /test/someElement[1]
xtrace: /test/someElement[1]/@n
xtrace: normalized-text( )
xtrace: /test/someElement[2]
xtrace: /test/someElement[2]/@n
xtrace: normalized-text( )
xtrace: /test/someElement[2]/someChild[1]
xtrace: normalized-text(someValue)
xtrace: normalized-text( )
xtrace: normalized-text( )
xtrace: /test/someElement[3]
xtrace: /test/someElement[3]/@n
xtrace: normalized-text( )
xtrace: /test/someElement[3]/someChild[1]
xtrace: normalized-text(someOtherValue)
xtrace: normalized-text( )
xtrace: normalized-text( )
xtrace: /test/someElement[4]
xtrace: /test/someElement[4]/@n
xtrace: normalized-text( )
xtrace: /test/someElement[4]/someChild[1]
xtrace: normalized-text(someValue)
xtrace: normalized-text( )
xtrace: normalized-text( )

XSLT 2.0

Die Aufrufe von dbg:trace können Sie mit dem Attribut use-when auf die gleiche Weise ein- und ausschalten, wie Sie es schon mit xsl:message im Rezept xsl:message effektiv einsetzen getan haben, sofern Ihr Prozessor das Testen von Systemeigenschaften unterstützt.

Saxon 8.4 unterstützt noch weitere Tracing-Optionen:

-TJ

Schaltet das Tracing des Bindings von Aufrufen externer Java-Methoden ein. Das ist dann nützlich, wenn Sie analysieren wollen, warum Saxon eine Java-Methode nicht finden kann, die zum Aufruf einer Erweiterungsfunktion im Stylesheet gehört, oder warum es eine bestimmte Methode statt einer anderen auswählt.

-TLclassname

Führt das Stylesheet mit dem angegebenen TraceListener aus. Dabei bezeichnet classname eine benutzerdefinierte Klasse, die net.sf.saxon.trace.TraceListener implementieren muss.

-TP

Führt das Stylesheet mit dem TraceListener TimedTraceListener aus. Hierbei wird eine Ausgabedatei mit Laufzeitmessungen für alle ausgeführten Anweisungen erzeugt. Diese Datei können Sie anschließend analysieren, um ein Laufzeitprofil für das Stylesheet zu erhalten.

Weitere Details finden Sie in der Dokumentation zu Saxon.

Diskussion

Den größten Nutzen haben Sie dann, wenn Sie diese Tracing-Technik mit einer Debugging-Ausgabe kombinieren, die angibt, wo Sie sich gerade im Stylesheet befinden. Das können Sie mit einer separaten Meldung bewirken, aber es gibt auch einen Parameter in xtrace namens tag, der statt des Default-Tags ausgegeben wird, sofern er gesetzt ist.

Das folgende Beispiel gibt normalisierte Textknoten aus, d.h., die Trace-Ausgabe geht nicht über mehrere Zeilen. Diesen Filter können Sie ganz leicht entfernen, wenn Sie den ganzen Text sehen möchten.

Beim Einsatz von Dokument-Tracing sollten Sie sich genau überlegen, wo Sie Ihre Trace-Aufrufe platzieren. Am sinnvollsten ist die Stelle bzw. sind die Stellen, an denen der aktuelle Knoten tatsächlich bearbeitet wird. Betrachten Sie das folgende Stylesheet für eine Postfix-Traversierung, die aus dem Rezept Durchführen eines Postorder Traversal stammt und an dieser Stelle mit einem Trace versehen wurde:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dbg="http://www.ora.com/XSLTCookbook/ns/debug">
  <xsl:include href="xtrace.xslt"/>
  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="/employee" priority="10">
    <xsl:apply-templates/>
    <xsl:call-template name="dbg:trace"/>
    <xsl:value-of select="@name"/>
    <xsl:text> ist der Chef des Unternehmens. </xsl:text>
    <xsl:call-template name="reportsTo"/>
    <xsl:call-template name="HimHer"/>
    <xsl:text>. </xsl:text>
    <xsl:text>&#xa;&#xa;</xsl:text>
  </xsl:template>
  <xsl:template match="employee[employee]">
    <xsl:apply-templates/>
    <xsl:call-template name="dbg:trace"/>
    <xsl:value-of select="@name"/>
    <xsl:text> ist ein Manager. </xsl:text>
    <xsl:call-template name="reportsTo"/>
    <xsl:call-template name="HimHer"/>
    <xsl:text>. </xsl:text>
    <xsl:text>&#xa;&#xa;</xsl:text>
  </xsl:template>
  <xsl:template match="employee">
    <xsl:call-template name="dbg:trace"/>
    <xsl:text>Niemand berichtet </xsl:text>
    <xsl:value-of select="@name"/>
    <xsl:text>. &#xa;</xsl:text>
  </xsl:template>
  <!-- Rest ausgelassen ... -->
</xsl:stylesheet>

Beachten Sie, wie der Trace beim Bearbeiten des aktuellen Knotens und nicht als erste Anweisung jedes Templates ausgeführt wird. An dieser Stelle platziert, ergibt sich der folgende Ablauf, der einer korrekten Postfix-Traversierung entspricht:

xtrace: /employee/employee[1]/employee[1]
xtrace: /employee/employee[1]/employee[2]/employee[1]
xtrace: /employee/employee[1]/employee[2]
xtrace: /employee/employee[1]
xtrace: /employee/employee[2]/employee[1]
xtrace: /employee/employee[2]/employee[2]/employee[1]
xtrace: /employee/employee[2]/employee[2]/employee[2]
xtrace: /employee/employee[2]/employee[2]/employee[3]
xtrace: /employee/employee[2]/employee[2]
xtrace: /employee/employee[2]
xtrace: /employee/employee[3]/employee[1]/employee[1]
xtrace: /employee/employee[3]/employee[1]/employee[2]
xtrace: /employee/employee[3]/employee[1]/employee[3]
xtrace: /employee/employee[3]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[1]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[2]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[2]/employee[2]
xtrace: /employee/employee[3]/employee[2]/employee[2]
xtrace: /employee/employee[3]/employee[2]/employee[3]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[3]
xtrace: /employee/employee[3]/employee[2]
xtrace: /employee/employee[3]
xtrace: /employee

Hätten Sie das Trace einfach in die erste Zeile gesetzt, dann hätten Sie folgenden irreführenden Ablauf erhalten, der einer Präfix-Traversierung entspricht:

xtrace: /employee
xtrace: /employee/employee[1]
xtrace: /employee/employee[1]/employee[1]
xtrace: /employee/employee[1]/employee[2]
xtrace: /employee/employee[1]/employee[2]/employee[1]
xtrace: /employee/employee[1]/employee[2]
xtrace: /employee/employee[1]
xtrace: /employee/employee[2]
xtrace: /employee/employee[2]/employee[1]
xtrace: /employee/employee[2]/employee[2]
xtrace: /employee/employee[2]/employee[2]/employee[1]
xtrace: /employee/employee[2]/employee[2]/employee[2]
xtrace: /employee/employee[2]/employee[2]/employee[3]
xtrace: /employee/employee[2]/employee[2]
xtrace: /employee/employee[2]
xtrace: /employee/employee[3]
xtrace: /employee/employee[3]/employee[1]
xtrace: /employee/employee[3]/employee[1]/employee[1]
xtrace: /employee/employee[3]/employee[1]/employee[2]
xtrace: /employee/employee[3]/employee[1]/employee[3]
xtrace: /employee/employee[3]/employee[1]
xtrace: /employee/employee[3]/employee[2]
xtrace: /employee/employee[3]/employee[2]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[1]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[2]
xtrace: /employee/employee[3]/employee[2]/employee[2]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[2]/employee[2]
xtrace: /employee/employee[3]/employee[2]/employee[2]
xtrace: /employee/employee[3]/employee[2]/employee[3]
xtrace: /employee/employee[3]/employee[2]/employee[3]/employee[1]
xtrace: /employee/employee[3]/employee[2]/employee[3]
xtrace: /employee/employee[3]/employee[2]
xtrace: /employee/employee[3]

Siehe auch

CatchXSL ist ein gratis verfügbares Werkzeug, das Laufzeitmessungen von XSL-Transformationen auf prozessorabhängige Weise durchführt. Während der Transformation werden alle XSLT-Anweisungen aufgezeichnet und als Stilereignisse mit einem Zeitstempel gespeichert. Die so entstehende Statistik enthält Informationen über den Fortlauf der Transformation und damit nützliche Hinweise für Verbesserungen des Stylesheets. Eine detaillierte Auflistung der Stilereignisse macht Angaben zu jedem einzelnen Schritt und seiner zeitlichen Dauer. Eine Auflistung nach Templates enthält die Zeit, die für jedes Template benötigt wurde, und bietet hiermit Hinweise auf Zeit fressende »Hot Spots« im Stylesheet.

  

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2006 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT Kochbuch" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O'Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de