2008-10-08 12 views
35

Ich versuche, eine XML-Datei in das Markup zu konvertieren, das von dokuwiki mit XSLT verwendet wird. Dies funktioniert bis zu einem gewissen Grad, aber der Einzug in der XSL-Datei wird in die Ergebnisse eingefügt. Im Moment habe ich zwei Möglichkeiten: Verlasse diese XSLT-Sache vollständig und finde einen anderen Weg, um von XML in Dokuwiki-Markup zu konvertieren, oder lösche etwa 95% des Whitespace aus der XSL-Datei, was es nahezu unlesbar und zu einem Wartungsalbtraum macht.Konvertieren von XML in reinen Text - wie sollte ich Leerzeichen in der XSLT ignorieren/behandeln?

Gibt es eine Möglichkeit, die Einrückung in der XSL-Datei beizubehalten, ohne den ganzen Whitespace auf das endgültige Dokument zu übertragen?

Hintergrund: Ich migriere ein Autodoc-Tool von statischen HTML-Seiten zu Dokuwiki, sodass die vom Serverteam entwickelte API vom Anwendungsteam weiter dokumentiert werden kann, wenn das App-Team schlecht dokumentierten Code ausführt. Die Logik besteht darin, einen Abschnitt jeder Seite für das Autodoc-Werkzeug zur Verfügung zu stellen und Kommentare außerhalb dieses Blocks zuzulassen. Ich benutze XSLT, weil wir bereits die XSL-Datei haben, um von XML zu XHTML zu konvertieren, und ich nehme an, dass es schneller ist, die XSL neu zu schreiben, als meine eigene Lösung von Grund auf zu rollen.

Bearbeiten: Ah, richtig, töricht mich, ich habe das Einzug Attribut vernachlässigt. (Anderer Hintergrund Hinweis: Ich bin neu in XSLT.) Andererseits muss ich immer noch mit Zeilenumbrüchen umgehen. Dokuwiki verwendet Pipes, um zwischen Tabellenspalten zu unterscheiden, was bedeutet, dass alle Daten in einer Tabellenzeile in einer Zeile stehen müssen. Gibt es eine Möglichkeit, zu unterdrücken, dass neue Zeilen ausgegeben werden (nur gelegentlich), so dass ich eine ziemlich komplexe Logik für jede Tabellenzelle in einer etwas lesbaren Form ausführen kann?

Antwort

75

Es gibt drei Gründe für im Ergebnis einer XSLT-Transformation unerwünschte Leerzeichen bekommen:

  1. Leerzeichen, die von zwischen den Knoten in dem Quelldokument
  2. Leerzeichen kommt, die von innen Knoten im Quelldokument
  3. Leerzeichen kommt, die

Ich werde reden über alle drei aus dem Stylesheet kommt Da es schwierig sein kann zu sagen, woher Whitespace kommt, müssen Sie möglicherweise mehrere Strategien verwenden.

den Leerraum zu adressieren, die zwischen den Knoten in Ihrem Quelldokument ist, sollten Sie <xsl:strip-space> verwenden alle Leerzeichen Streifen aus, die zwischen zwei Knoten angezeigt wird, und dann <xsl:preserve-space> verwenden, um die signifikanten Leerzeichen zu erhalten, die in gemischtem Inhalt erscheinen.Zum Beispiel, wenn Ihr Quelldokument wie folgt aussieht:

<ul> 
    <li>This is an <strong>important</strong> <em>point</em></li> 
</ul> 

dann werden Sie die Leerzeichen zwischen den <ul> und den <li> und zwischen den </li> und den </ul>, zu ignorieren, die nicht signifikant ist, aber die Leerzeichen zwischen dem wahren <strong> und <em> Elemente, die ist signifikant (sonst würden Sie bekommen "Dies ist ein ** wichtiger *** Punkt *"). Gehen Sie

diese Verwendung
<xsl:strip-space elements="*" /> 
<xsl:preserve-space elements="li" /> 

Das elements Attribut auf <xsl:preserve-space> sollten grundsätzlich alle Elemente in Ihrem Dokument aufgelistet, die Inhalte gemischt haben.

Abgesehen: mit <xsl:strip-space> reduziert auch die Größe des Quellbaum im Speicher und macht Ihr Sheet effizienter zu gestalten, so lohnt es sich, zu tun, auch wenn Sie keine Leerzeichen Probleme dieser Art verfügen.

Um den Leerraum zu adressieren, der innerhalb der Knoten in Ihrem Quelldokument angezeigt wird, sollten Sie normalize-space() verwenden. Zum Beispiel, wenn Sie haben:

<dt> 
    a definition 
</dt> 

und Sie können sicher sein, dass das <dt> Element werden keine Elemente enthalten, die Sie mit etwas tun wollen, dann können Sie tun:

<xsl:template match="dt"> 
    ... 
    <xsl:value-of select="normalize-space(.)" /> 
    ... 
</xsl:template> 

Die Führende und nachfolgende Leerzeichen werden vom Wert des Elements <dt> entfernt und Sie erhalten nur die Zeichenfolge "a definition".

Leerzeichen aus dem Sheet kommen zu adressieren, die Sie erleben vielleicht derjenige ist, ist, wenn Sie Text in einer Vorlage wie dieses:

<xsl:template match="name"> 
    Name: 
    <xsl:value-of select="." /> 
</xsl:template> 

XSLT-Stylesheets in der gleichen Weise wie die analysiert werden Quelldokumente, die sie verarbeiten, sodass das obige XSLT als eine Struktur interpretiert wird, die ein <xsl:template>-Element mit einem match-Attribut enthält, dessen erstes Kind ein Textknoten und dessen zweites Kind ein <xsl:value-of>-Element mit einem select-Attribut ist. Der Textknoten hat führende und nachfolgende Leerzeichen (einschließlich Zeilenumbrüche). Da es sich um einen literalen Text im Stylesheet handelt, wird es buchstäblich in das Ergebnis kopiert, mit allen führenden und nachfolgenden Leerzeichen.

Aber einige Leerzeichen in XSLT Stylesheets werden automatisch entfernt, nämlich diejenigen zwischen den Knoten. Sie erhalten keinen Zeilenumbruch in Ihrem Ergebnis, da zwischen dem <xsl:value-of> und dem Ende des <xsl:template> ein Zeilenumbruch vorliegt.

Um nur den Text, den Sie im Ergebnis wollen zu erhalten, verwenden Sie das <xsl:text> Element wie folgt:

<xsl:template match="name"> 
    <xsl:text>Name: </xsl:text> 
    <xsl:value-of select="." /> 
</xsl:template> 

Der XSLT-Prozessor wird die Zeilenumbrüche und Einzüge, die innerhalb zwischen den Knoten, und nur dann ausgegeben, der Text erscheinen ignorieren das <xsl:text> Element.

+0

Das war extrem hilfreich! Vielen Dank. – Black

+0

das war in der Tat hilfreich, aber ich bin verwirrt, wenn Sie den Ausdruck "zwischen Knoten" verwenden. Ist es nicht wahr, dass alle Leerzeichen in Textknoten enthalten sind? Was meinst du mit "zwischen Knoten"? Wenn ich Ihren Namen nicht erkannt hätte, hätte ich angenommen, dass Sie eine Vorlesung über XML-Dokumentenstruktur benötigen. – LarsH

+0

Guter Artikel, danke! Aber streng genommen verwenden Sie den Begriff "Knoten", wo Sie eigentlich "Element" meinen. – rustyx

4

Verwenden Sie indent = "no" in Ihrem Ausgabe-Tag?

<xsl:output method="text" indent="no" /> 

Auch wenn Sie xsl verwenden: value-of Sie das disable-output-escaping verwenden = "yes" mit einigen Leerzeichen Problemen zu helfen.

+4

Meistens ist die Verwendung von 'disable-output-escaping' die falsche Methode. Es ist nur für sehr eingeschränkte Situationen da. Es ist wahrscheinlich schädlicher als hilfreich, d-o-e so allgemein auf jemanden anzusprechen, der es nicht besser kennt. Siehe http://www.dpawson.co.uk/xsl/sect2/N2215.html#d3702e223 – LarsH

0

In Bezug auf Ihr bearbeiten über neue Linien, können Sie diese Vorlage verwenden, um rekursiv eine Zeichenfolge innerhalb einer anderen Zeichenfolge zu ersetzen, und man kann es für die Pausen Zeile:

<xsl:template name="replace.string.section"> 
    <xsl:param name="in.string"/> 
    <xsl:param name="in.characters"/> 
    <xsl:param name="out.characters"/> 
    <xsl:choose> 
    <xsl:when test="contains($in.string,$in.characters)"> 
     <xsl:value-of select="concat(substring-before($in.string,$in.characters),$out.characters)"/> 
     <xsl:call-template name="replace.string.section"> 
     <xsl:with-param name="in.string" select="substring-after($in.string,$in.characters)"/> 
     <xsl:with-param name="in.characters" select="$in.characters"/> 
     <xsl:with-param name="out.characters" select="$out.characters"/> 
     </xsl:call-template> 
    </xsl:when> 
    <xsl:otherwise> 
     <xsl:value-of select="$in.string"/> 
    </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

Nennen Sie es wie folgt (in diesem Beispiel ersetzt Linie Brüche in der $ some.string Variable mit Leerzeichen):

<xsl:call-template name="replace.string.section"> 
     <xsl:with-param name="in.string" select="$some.string"/> 
     <xsl:with-param name="in.characters" select="'&#xA;'"/> 
     <xsl:with-param name="out.characters" select="' '"/> 
    </xsl:call-template> 
3

@ JeniT Antwort ist großartig, ich möchte nur einen Trick für die Verwaltung von Leerzeichen aufzeigen. Ich bin mir nicht sicher, ob es der beste Weg (oder sogar ein guter Weg) ist, aber es funktioniert für mich im Moment.

(. "S" für die Raumfahrt, "e" für leere, "n" für die Neue-Zeile)

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE xsl:transform [ 
    <!ENTITY s "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> </xsl:text>" > 
    <!ENTITY s2 "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> </xsl:text>" > 
    <!ENTITY s4 "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> </xsl:text>" > 
    <!ENTITY s6 "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>  </xsl:text>" > 
    <!ENTITY e "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'></xsl:text>" > 
    <!ENTITY n "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> 
</xsl:text>" > 
]> 

<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"> 
<xsl:output method="text"/> 
<xsl:template match="/"> 
    &e;Flush left, despite the indentation.&n; 
    &e; This line will be output indented two spaces.&n; 

     <!-- the blank lines above/below won't be output --> 

    <xsl:for-each select="//foo"> 
    &e; Starts with two blanks: <xsl:value-of select="@bar"/>.&n; 
    &e; <xsl:value-of select="@baz"/> The 'e' trick won't work here.&n; 
    &s2;<xsl:value-of select="@baz"/> Use s2 instead.&n; 
    &s2; <xsl:value-of select="@abc"/> <xsl:value-of select="@xyz"/>&n; 
    &s2; <xsl:value-of select="@abc"/>&s;<xsl:value-of select="@xyz"/>&n; 
    </xsl:for-each> 
</xsl:template> 
</xsl:transform> 

Angewandt auf:

<?xml version="1.0" encoding="UTF-8"?> 
<foo bar="bar" baz="baz" abc="abc" xyz="xyz"></foo> 

Ausgänge:

Flush left, despite the indentation. 
    This line will be output indented two spaces. 
    Starts with two blanks: bar. 
baz The 'e' trick won't work here. 
    baz Use s2 instead. 
    abcxyz 
    abc xyz 

Der Trick "e" funktioniert vor einem Textknoten, der mindestens ein Nicht-Leerzeichen enthält, weil er folgendermaßen erweitert wird:

<xsl:template match="/"> 
    <xsl:text></xsl:text>Flush left, despite the indentation.<xsl:text> 
</xsl:text> 

Da die rules for stripping whitespace sagen, dass durch Leerzeichen nur Textknoten abgezogen bekommen, die Newline und Vertiefung zwischen dem < xsl: template > und < xsl: text > gestrippt erhalten (gut). Da die Regeln sagen, dass ein Textknoten mit mindestens einem Leerzeichen erhalten bleibt, behält der implizite Textknoten, der " This line will be output indented two spaces." enthält, seinen führenden Leerraum (ich nehme an, dies hängt auch von den Einstellungen für strip/preserve/normalize ab). Die "& n;" Am Ende der Zeile wird eine neue Zeile eingefügt, aber es wird auch sichergestellt, dass alle folgenden Leerzeichen ignoriert werden, da sie zwischen zwei Knoten angezeigt werden.

Das Problem, das ich habe, ist, wenn ich eine eingerückte Zeile ausgeben möchte, die mit einem < xsl: value-of > beginnt. In diesem Fall ist die "& e;" wird nicht helfen, da der Einrückungs-Whitespace nicht mit irgendwelchen Nicht-Leerzeichen-Zeichen "verbunden" ist. Für diese Fälle verwende ich "& s2;" oder "& s4;", abhängig davon, wie viel Eindruck ich möchte.

Es ist ein hässlicher Hack, den ich sicher bin, aber zumindest habe mich nicht den ausführlichen „< xsl: text >“ Tags meine XSLT Littering, und zumindest kann ich einrücken noch den XSLT selbst, so dass es lesbar ist. Ich habe das Gefühl, dass ich XSLT für etwas missbrauche, für das es nicht entwickelt wurde (Textverarbeitung), und das ist das Beste, was ich tun kann.


Edit: In Reaktion auf die Kommentare, das ist, was aussieht wie es ohne die „Makros“:

<xsl:template match="/"> 
    <xsl:text>Flush left, despite the indentation.</xsl:text> 
    <xsl:text> This line will be output indented two spaces.</xsl:text> 
    <xsl:for-each select="//foo"> 
    <xsl:text> Starts with two blanks: </xsl:text><xsl:value-of select="@bar"/>.<xsl:text> 
</xsl:text> 
    <xsl:text> </xsl:text><xsl:value-of select="@abc"/><xsl:text> </xsl:text><xsl:value-of select="@xyz"/><xsl:text> 
</xsl:text> 
    </xsl:for-each> 
</xsl:template> 

Ich denke, das macht es weniger klar die beabsichtigte Ausgabe Vertiefung zu sehen, und es verschraubt die Einrückung der XSL selbst, weil die </xsl:text> End-Tags in Spalte 1 der XSL-Datei angezeigt werden müssen (sonst erhalten Sie unerwünschte Leerzeichen in der Ausgabedatei).

+0

@Dan: Erstens,' xsl: text' ist nicht ausführlich und Sie können concat immer für 'xsl: value verwenden -of ". Zweitens verarbeiten Sie keinen Text, Ihre Ausgabe ist reiner Text. –

+0

@Dan: Last. Ihre Lösung ist gegen XSLT, weil diese Entitäten (richtig deklariert) Teil der Oberflächensyntax des XML-Dokuments sind (das Stylesheet, In diesem Fall benötigt die Ersetzung Zeit in der Parsing-Phase, bevor sie den XSLT-Prozessor erreicht.Nachdem die Ersetzung durchgeführt wurde und es ** neue Elemente ** im Stylesheet gibt, löschen die Regeln zum Entfernen/Aufbewahren nur Textknoten Aus der Sicht eines Lesers wird es nicht klar sein, was Ihr Stylesheet-Ergebnis wäre –

+0

@Alejandro: Danke für die Rückmeldung Ich nehme an, es ist nicht ausführlich, wenn Sie bereits an XML gewöhnt sind ... mein Hintergrund ist mehr lex/yacc/C++, also fühle ich mich definitiv von meinem Element hier. Ich denke, die Verwendung eines XML-Editors im Vergleich zu einem Texteditor könnte helfen. – Dan