Der Umgang mit Whitespace

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

Sie müssen XML in formatierten Text umwandeln, allerdings werden die Ergebnisse durch Probleme mit Whitespace ruiniert.

Lösung

Schauen Sie sich das folgende annotierte XML-Beispiel an. Die Symbole ↵ (Newline; neue Zeile), → (Tabulator) und ❑ (Leerzeichen) markieren nur aus Whitespace bestehende Knoten, die häufig übersehen werden, aber dennoch in die Ausgabe kopiert werden:

<review>↵
→<author>↵
→→ <name>Sal Mangano</name>↵
→→<email>smangano@somewhere.com</email>↵
→</author>↵
<title>XSLT Kochbuch</title>↵
<reviewer>↵
→→<name>❑<anonymous/>❑</name>↵
→→<email>smangano@somewhere.com</email>↵
→→<comment>↵
Absolut brillant. <b>Jeden Cent wert!</b>❑<i>Müssen Sie kaufen, weil ich ↵
den Autor persönlich kenne und er das Geld sicher gebrauchen kann.</i>↵
→→</comment>↵
→ </reviewer>↵
</review>↵

Zu viel Whitespace

  1. Setzen Sie xsl:strip-space ein, um Knoten loszuwerden, die nur aus Whitespace bestehen.

    Dieses Top-Level-Element mit einem einzigen Attribut, elements, wird einer durch Whitespace getrennten Liste von Elementnamen zugewiesen, aus der Sie überzähligen Whitespace entfernen wollen. Überzähliger Whitespace bedeutet hier Textknoten, die nur aus Whitespace bestehen. Das heißt zum Beispiel, dass der Whitespace, der die Wörter im gezeigten comment-Element trennt, wichtig ist, weil dieses Element nicht nur aus Whitespace besteht. Andererseits handelt es sich bei dem Whitespace, der durch die besonderen Symbole gekennzeichnet ist, um Knoten, die nur aus Whitespace bestehen.

    Eine gebräuchliche Wendung benutzt <xsl:strip-space elements="*"/>, um Whitespace standardmäßig zu entfernen, und xsl:preserve-space (siehe unten), um bestimmte Elemente zu überschreiben. In XSLT 2.0 dürfen Sie elements="*:Name" haben, wodurch dem Prozessor mitgeteilt wird, dass er Whitespace in allen Elementen mit dem angegebenen lokalen Namen entfernen soll, ungeachtet des Namensraums.

  2. Verwenden Sie normalize-space, um überzähligen Whitespace zu entfernen.

    Oft wird fälschlicherweise angenommen, dass xsl:strip-space sich um »überzähligen« Whitespace kümmert, wie etwa um solchen, der eingesetzt wird, um den Text im gezeigten comment-Element auszurichten. Das ist nicht der Fall. Der Parser beachtet immer den wichtigen Whitespace innerhalb des Textes eines Elements, der mit Nicht-Whitespace gemischt ist. Um dieses zusätzliche Leerzeichen zu entfernen, benutzen Sie normalize-space, wie in <xsl:value-of select="normalize-space(comment)"/>.

  3. Verwenden Sie translate, um allen Whitespace loszuwerden.

    Ein anderer verbreiteter Fehler besteht darin anzunehmen, dass normalize-space allen Whitespace entfernt. Das ist nicht der Fall. Stattdessen entfernt es nur führenden und abschließenden Whitespace und konvertiert mehrfache interne Whitespace-Zeichen in einzelne Leerzeichen. Falls Sie den gesamten Whitespace entfernen müssen, verwenden Sie translate(irgendwas, '&#x20;&#xa;&#xd; &#x9;', '').

  4. Verwenden Sie ein leeres xsl:text-Element, um zu verhindern, dass abschließender Whitespace im Stylesheet als relevant angesehen wird. xsl:text wird normalerweise als eine Methode betrachtet, um Whitespace zu schützen. Allerdings kann ein strategisch platziertes, leeres xsl:text-Element dafür sorgen, dass abschließender Whitespace im Stylesheet nicht als wichtig interpretiert wird.

Betrachten Sie die Ergebnisse der beiden Modi im folgenden Dokument und Stylesheet, die in den beiden folgenden Beispielen gezeigt werden.

Beispiel: Eingabe.

<numbers>
  <number>10</number>
  <number>3.5</number>
  <number>4.44</number>
  <number>77.7777</number>
</numbers>

Beispiel: Verarbeitung der Zahlen mit einem leeren xsl:text-Element und ohne ein leeres xsl:text-Element.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="numbers">
    Ohne leeres text-Element:
    <xsl:apply-templates mode="without"/>
    Mit leerem text-Element:
    <xsl:apply-templates mode="with"/>
  </xsl:template>
  <xsl:template match="number" mode="without">
    <xsl:value-of select="."/>,
  </xsl:template>
  <xsl:template match="number" mode="with">
    <xsl:value-of select="."/>,<xsl:text/>
  </xsl:template>
</xsl:stylesheet>

Beispiel: Ausgabe.

Ohne leeres text-Element:
10,
3.5,
4.44,
77.7777,

Mit leerem text-Element:
10,3.5,4.44,77.7777,

Beachten Sie, dass xsl:text nichts Magisches an sich hat, wenn es auf diese Weise benutzt wird. Es funktioniert genauso gut, wenn Sie <xsl:text/> durch <xsl:if test="0"/> ersetzen (allerdings sollten Sie das nur tun, wenn es Ihnen gefällt, andere Leute zu verwirren). Damit platzieren Sie einen Elementknoten zwischen das Komma und das abschließende Newline, wodurch ein nur aus Whitespace bestehender Knoten erzeugt wird, der ignoriert wird. Natürlich finden manche das verwirrend, Sie können daher auch <xsl:text>,</xsl:text> schreiben, falls Ihnen das lieber ist.

Zu wenig Whitespace

  1. Verwenden Sie xsl:preserve-space, um für bestimmte Elemente xsl:strip-space außer Kraft zu setzen.

    Es ist nicht besonders sinnvoll, xsl:preserve-space einzusetzen, wenn Sie nicht auch xsl:strip-space benutzen. Dies liegt daran, dass das Standardverhalten darin besteht, Leerzeichen im Eingabedokument und in Dokumenten, die mit der Funktion document( ) geladen wurden, zu schützen.

    Achtung!
    Merken Sie sich, dass MSXML standardmäßig Textknoten entfernt, die nur aus Whitespace bestehen. In diesem Fall können Sie xsl:preserve-space einsetzen, um diesem Fehler entgegenzuwirken.

  2. Verwenden Sie xsl:text, um die Leerzeichenverwendung im Ausgabetext exakt festzulegen.

    Der gesamte Whitespace innerhalb eines xsl:text-Elements wird geschützt. Dieser Schutz erlaubt eine genaue Kontrolle über die Platzierung von Whitespace. Manchmal können Sie xsl:text verwenden, um einfach Zeilenumbrüche einzufügen:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="number">
    <xsl:value-of select="."/>
    <xsl:text>&#xa;</xsl:text>
  </xsl:template>
</xsl:stylesheet>

Das Problem beim direkten Ausgeben von Newline-Zeichen besteht darin, dass auf einigen Plattformen (z. B. Microsoft-Plattformen) erwartet wird, dass ein Zeilenumbruch als Carriage-Return plus Newline dargestellt wird. Da jedoch von XML-Parsern verlangt wird, Carriage-Return plus Newline in ein einziges Newline umzuwandeln, gibt es keine Möglichkeit, ein plattformunabhängiges Stylesheet zu erzeugen. Glücklicherweise behandeln die meisten Windows-basierten Editoren und die Windows-Eingabeaufforderung einzelne Newlines korrekt. Die einzige Ausnahme bildet der Editor Notepad, der kostenlos in Windows enthalten ist.

  1. Verwenden Sie geschützte Leerzeichen.

    XSLT behandelt das Zeichen #xA0; (geschütztes Leerzeichen) nicht als normalen Whitespace. Vor allem ignorieren sowohl xsl:strip-space als auch normalize-space( ) dieses Zeichen. Falls Sie in den meisten Fällen Whitespace entfernen müssen, es aber einige bestimmte Instanzen gibt, bei denen Whitespace erhalten bleiben soll, sollten Sie einmal versuchen, dieses Zeichen in der XML-Eingabe zu verwenden. Geschützte Leerzeichen sind speziell für HTML-Ausgaben sinnvoll. In anderen Kontexten sind sie nicht ganz so nützlich (je nachdem, wie der Renderer damit umgeht).

Diskussion

Die Lösung listet Techniken für den Umgang mit Whitespace auf. Es ist dennoch sinnvoll, die XSLT-Regeln zu kennen, die diesen Techniken zugrunde liegen.

Die wichtigsten Regeln gelten für beide: das Stylesheet und das Eingabedokument (bzw. die Eingabedokumente):

  1. Ein Textknoten wird niemals entfernt, es sei denn, er enthält nur Whitespace-Zeichen (#x20, #x9, #xD oder #xA).

    Auch wenn sie nicht so verbreitet sind, sollten Sie die Wirkung der xml:space-Attribute im Stylesheet und in den Eingabedokumenten kennen.

  2. Wenn das Vorgängerelement eines Textknotens ein xml:space-Attribut mit dem Wert preserve enthält und kein nähergelegenes Vorgängerelement das Attribut xml:space mit dem Wert default besitzt, dann werden Textknoten, die nur Whitespace enthalten, nicht entfernt.

    In diesem Kapitel werden die Regeln für Stylesheets und Quelldokumente getrennt betrachtet. Bei Stylesheets sind Ihre Möglichkeiten einfach.

  3. Die einzigen Stylesheet-Elemente, bei denen nur aus Whitespace bestehende Knoten standardmäßig geschützt werden, sind xsl:text. Hier bedeutet »standardmäßig«, »solange es nicht anderweitig mit xml:space="preserve" angegeben ist«, wie in Schritt 2 beschrieben (siehe die beiden folgenden Beispiele).

Beispiel: Stylesheet, das die Wirkung von xsl:text und xml:space="preserve" demonstriert.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="numbers">
    Ohne xml:space="preserve":
    <xsl:apply-templates mode="without-preserve"/>
    Mit xml:space="preserve":
    <xsl:apply-templates mode="with-preserve"/>
  </xsl:template>
  <xsl:template match="number" mode="without-preserve">
    <xsl:value-of select="."/><xsl:text> </xsl:text>
  </xsl:template>
  <xsl:template match="number" mode="with-preserve" xml:space="preserve">
    <xsl:value-of select="."/><xsl:text> </xsl:text>
  </xsl:template>
</xsl:stylesheet>

Beispiel: Ausgabe.

Ohne xml:space="preserve":
10 3.5 4.44 77.7777
Mit xml:space="preserve":

10

3.5

4.44

77.7777

Der einzige Whitespace, der in der ersten Version des Templates eingefügt wird, ist das einzelne Leerzeichen, das im xsl:text-Element enthalten ist. Wenn Sie dagegen im zweiten Template xml:space="preserve" verwenden, nehmen Sie alle Whitespace-Zeichen mit, die in dem Element enthalten sind, einschließlich der zwei Zeilenumbrüche (der erste folgt nach <xsl:template ...> und der zweite nach </xsl:text>).

Für Quelldokumente sehen die Regeln folgendermaßen aus:

  • Zu Anfang enthält die Liste der Elemente, in denen Whitespace geschützt werden soll, alle Elemente im Dokument.
  • Wenn ein Element einem NameTest in einem xsl:strip-space-Element entspricht, dann wird es aus der Liste der Whitespace-schützenden Elementnamen entfernt.
  • Wenn ein Elementname einem NameTest in einem xsl:preserve-space-Element entspricht, dann wird er zur Liste der whitespace-schützenden Elementnamen hinzugefügt.

Ein NameTest ist entweder ein einfacher Name (z.B. doc) oder ein Name mit einem Namensraumpräfix (z.B. my:doc), ein Jokerzeichen (z.B. *) oder ein Jokerzeichen mit einem Namensraumpräfix (z.B. my:*). In XSLT 2.0 ist *:doc ebenfalls erlaubt. Es gelten die vorgegebenen Prioritäts- und Importvorrangregeln, wenn Konflikte zwischen xml:strip-space und xml:preserve-space auftreten:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="http://www.ora.com/XSLTCookbook/ns/my">
  <!-- Whitespace in allen Elementen entfernen -->
  <xsl:strip-space elements="*"/>
  <!-- außer aus solchen im Namensraum "my" -->
  <xsl:preserve-space elements="my:*"/>
  <!-- und solchen namens foo -->
  <xsl:preserve-space elements="foo"/>
</xsl:stylesheet>

Siehe auch

Eine der vollständigsten Darstellungen zur Behandlung von Whitespace finden Sie in Michael Kays XSLT Programmer's Reference (Wrox, 2001). In diesem Buch werden auch die Importvorrangregeln ausführlich behandelt.

  

<< 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