53 Stimmen

XSLT - Leerzeichen aus der Vorlage entfernen

Ich verwende XML, um eine kleine Kontaktliste zu speichern, und versuche, eine XSL-Vorlage zu schreiben, die sie in eine CSV-Datei umwandelt. Das Problem, das ich habe, ist mit Leerzeichen in der Ausgabe.

Das Ergebnis:

Friend, John, Smith, Home,
        123 test,
       Sebastopol,
       California,
       12345,
     Home 1-800-123-4567, Personal john.smith@gmail.com

Ich habe sowohl die XML-Quelldatei als auch die zugehörige XSL-Vorlage eingerückt, um sie leichter lesen und entwickeln zu können, aber all der zusätzliche Leerraum wird in die Ausgabe übernommen. Die XML selbst hat keinen zusätzlichen Leerraum innerhalb der Knoten, nur außerhalb von ihnen für die Formatierung, und das gleiche gilt für die XSLT.

Damit die CSV-Datei gültig ist, muss jeder Eintrag in einer eigenen Zeile stehen und darf nicht unterbrochen sein. Neben dem Entfernen aller zusätzlichen Leerzeichen aus dem XML und XSLT (so dass sie nur eine lange Codezeile), gibt es einen anderen Weg, um loszuwerden, die Leerzeichen in der Ausgabe?

Bearbeiten: Hier ist ein kleines XML-Beispiel:

<PHONEBOOK>
    <LISTING>
        <FIRST>John</FIRST>
        <LAST>Smith</LAST>
        <ADDRESS TYPE="Home">
            <STREET>123 test</STREET>
            <CITY>Sebastopol</CITY>
            <STATE>California</STATE>
            <ZIP>12345</ZIP>
        </ADDRESS>
        <PHONE>1-800-123-4567</PHONE>
        <EMAIL>john.smith@gmail.com</EMAIL>
        <RELATION>Friend</RELATION>
    </LISTING>
</PHONEBOOK>

Und hier ist die XSLT:

<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />

 <xsl:template match="/">
   <xsl:for-each select="//LISTING">
    <xsl:value-of select="RELATION" /><xsl:text>, </xsl:text>
    <xsl:value-of select="FIRST" /><xsl:text>, </xsl:text>
    <xsl:value-of select="LAST" /><xsl:text>, </xsl:text>

    <xsl:if test="ADDRESS">
     <xsl:for-each select="ADDRESS">
       <xsl:choose>
        <xsl:when test="@TYPE">
         <xsl:value-of select="@TYPE" />,
        </xsl:when>
            <xsl:otherwise>
            <xsl:text>Home </xsl:text>
            </xsl:otherwise>
       </xsl:choose>
       <xsl:value-of select="STREET" />,
       <xsl:value-of select="CITY" />,
       <xsl:value-of select="STATE" />,
       <xsl:value-of select="ZIP" />,
     </xsl:for-each>
    </xsl:if>

    <xsl:for-each select="PHONE">
      <xsl:choose>
       <xsl:when test="@TYPE">
        <xsl:value-of select="@TYPE" />  
       </xsl:when>
       <xsl:otherwise><xsl:text>Home </xsl:text></xsl:otherwise>
      </xsl:choose>
     <xsl:value-of select="."  /><xsl:text  >, </xsl:text>
    </xsl:for-each>

    <xsl:if test="EMAIL">
     <xsl:for-each select="EMAIL">
      <xsl:choose>
       <xsl:when test="@TYPE">
        <xsl:value-of select="@TYPE" /><xsl:text  > </xsl:text> 
       </xsl:when>
       <xsl:otherwise><xsl:text  >Personal </xsl:text></xsl:otherwise>
      </xsl:choose>
      <xsl:value-of select="."  /><xsl:text  >, </xsl:text>
     </xsl:for-each>
    </xsl:if>
    <xsl:text>&#10;&#13;</xsl:text>
   </xsl:for-each>
 </xsl:template>

</xsl:stylesheet>

108voto

Tomalak Punkte 320467

In XSLT wird der Leerraum standardmäßig beibehalten, da es sich durchaus um relevante Daten handeln kann.

Der beste Weg, unerwünschten Leerraum in der Ausgabe zu vermeiden, ist, ihn gar nicht erst zu erzeugen. Tun Sie das nicht:

<xsl:template match="foo">
  foo
</xsl:template>

denn das ist "\n··foo\n" vom Standpunkt des Verarbeiters aus gesehen. Eher tun

<xsl:template match="foo">
  <xsl:text>foo</xsl:text>
</xsl:template>

Leerzeichen im Stylesheet werden ignoriert, solange sie nur zwischen XML-Elementen vorkommen. Einfach gesagt: Verwenden Sie niemals "nackten" Text in Ihrem XSLT-Code, sondern schließen Sie ihn immer in ein Element ein.

Auch die Verwendung eines unspezifischen:

<xsl:apply-templates />

ist problematisch, da die XSLT-Standardregel für Textknoten lautet: "Kopiere sie in die Ausgabe". Dies gilt auch für "Nur-Weißraum"-Knoten. Zum Beispiel:

<xml>
  <data> value </data>
</xml>

enthält drei Textknoten:

  1. "\n··" (gleich nach <xml> )
  2. "·value·"
  3. " \n" (kurz vor </xml> )

Um zu vermeiden, dass sich Nr. 1 und Nr. 3 in die Ausgabe einschleichen (was der häufigste Grund für unerwünschte Leerzeichen ist), können Sie die Standardregel für Textknoten außer Kraft setzen, indem Sie eine leere Vorlage deklarieren:

<xsl:template match="text()" />

Alle Textknoten sind nun stummgeschaltet und die Textausgabe muss explizit erstellt werden:

<xsl:value-of select="data" />

Um Leerzeichen aus einem Wert zu entfernen, können Sie die normalize-space() XSLT-Funktion:

<xsl:value-of select="normalize-space(data)" />

Aber Vorsicht, da die Funktion alle Leerzeichen in der Zeichenkette normalisiert, z. B. "·value··1·" werden würde "value·1" .

Zusätzlich können Sie die <xsl:strip-space> y <xsl:preserve-space> Elemente, obwohl dies normalerweise nicht notwendig ist (und ich persönlich bevorzuge die explizite Behandlung von Leerzeichen, wie oben beschrieben).

11voto

Noah Heldman Punkte 6524

Standardmäßig haben XSLT-Vorlagen <xsl:preserve-space> gesetzt, wodurch Leerzeichen in der Ausgabe erhalten bleiben. Sie können hinzufügen <xsl:strip-space elements="*"> um ihm zu sagen wo Leerzeichen zu löschen sind.

Möglicherweise müssen Sie auch eine normalize-space-Anweisung einfügen, etwa so:

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

Hier ist ein Beispiel für die Erhaltung des Raums aus W3-Schulen .

2voto

David Andres Punkte 30563

Was das Entfernen von Tabulatoren, aber die Beibehaltung separater Zeilen betrifft, so habe ich den folgenden XSLT 1.0-Ansatz ausprobiert, und er funktioniert recht gut. Ob Sie die Version 1.0 oder 2.0 verwenden, hängt weitgehend davon ab, welche Plattform Sie verwenden. Es sieht so aus, als ob die .NET-Technologie immer noch von XSLT 1.0 abhängt, so dass Sie auf extrem unübersichtliche Vorlagen beschränkt sind (siehe unten). Wenn Sie Java oder etwas anderes verwenden, beachten Sie bitte den viel saubereren XSLT 2.0 Ansatz, der ganz unten aufgeführt ist.

Diese Beispiele sollen von Ihnen erweitert werden, um Ihren spezifischen Bedürfnissen gerecht zu werden. Ich verwende hier die Registerkarten als Beispiel, aber das sollte allgemein genug sein, um erweiterbar zu sein.

XML:

<?xml version="1.0" encoding="UTF-8"?>
<text>
        adslfjksdaf

                dsalkfjdsaflkj

            lkasdfjlsdkfaj
</text>

...und die XSLT 1.0-Vorlage (erforderlich, wenn Sie .NET verwenden):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet  
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">   
 <xsl:template name="search-and-replace">
   <xsl:param name="input"/>
   <xsl:param name="search-string"/>
   <xsl:param name="replace-string"/>
   <xsl:choose>
    <xsl:when test="$search-string and 
                    contains($input,$search-string)">
       <xsl:value-of
           select="substring-before($input,$search-string)"/>
       <xsl:value-of select="$replace-string"/>
       <xsl:call-template name="search-and-replace">
         <xsl:with-param name="input"
               select="substring-after($input,$search-string)"/>
         <xsl:with-param name="search-string"
               select="$search-string"/>
         <xsl:with-param name="replace-string"
               select="$replace-string"/>
       </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$input"/>
    </xsl:otherwise>
   </xsl:choose>
  </xsl:template>                
  <xsl:template match="text">
   <xsl:call-template name="search-and-replace">
     <xsl:with-param name="input" select="text()" />
     <xsl:with-param name="search-string" select="'&#x9;'" />
     <xsl:with-param name="replace-string" select="''" />
   </xsl:call-template>    
  </xsl:template>
</xsl:stylesheet>

XSLT 2.0 macht dies trivial mit der replace Funktion:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      exclude-result-prefixes="xs"
      version="2.0">
 <xsl:template match="text">
  <xsl:value-of select="replace(text(), '&#x9;', '')" />
 </xsl:template>
</xsl:stylesheet>

1voto

Pavel Minaev Punkte 97251

Andere haben bereits auf das allgemeine Problem hingewiesen. Spezifisch für Ihr Stylesheet ist, dass Sie vergessen haben <xsl:text> für Kommas:

   <xsl:choose>
    <xsl:when test="@TYPE">
     <xsl:value-of select="@TYPE" />,
    </xsl:when>
    <xsl:otherwise>Home </xsl:otherwise>
   </xsl:choose>
   <xsl:value-of select="STREET" />,
   <xsl:value-of select="CITY" />,
   <xsl:value-of select="STATE" />,
   <xsl:value-of select="ZIP" />,

Dadurch wird das Leerzeichen nach jedem Komma wichtig und landet in der Ausgabe. Wenn Sie jedes Komma in <xsl:text> verschwindet das Problem.

Außerdem sollten Sie das loswerden disable-output-escaping . Es bringt hier nichts, da Sie kein XML ausgeben.

1voto

Nick Groznykh Punkte 19

Meine vorherige Antwort ist falsch, alle Kommas müssen über den Tag 'text' ausgegeben werden.

<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:template match="/PHONEBOOK">
        <xsl:for-each select="LISTING">
            <xsl:value-of select="RELATION" /><xsl:text>, </xsl:text>
            <xsl:value-of select="FIRST" /><xsl:text>, </xsl:text>
            <xsl:value-of select="LAST" /><xsl:text>, </xsl:text>

                <xsl:for-each select="ADDRESS">
                    <xsl:choose>
                        <xsl:when test="@TYPE">
                            <xsl:value-of select="@TYPE" /><xsl:text>,</xsl:text>
                        </xsl:when>
                        <xsl:otherwise><xsl:text>Home </xsl:text></xsl:otherwise>
                    </xsl:choose>
                <xsl:value-of select="STREET/text()" /><xsl:text>,</xsl:text>
                    <xsl:value-of select="CITY/text()" /><xsl:text>,</xsl:text>
                    <xsl:value-of select="STATE/text()" /><xsl:text>,</xsl:text>
                    <xsl:value-of select="ZIP/text()" /><xsl:text>,</xsl:text>
                </xsl:for-each>

            <xsl:for-each select="PHONE">
                <xsl:choose>
                    <xsl:when test="@TYPE">
                        <xsl:value-of select="@TYPE" />  
                    </xsl:when>
                    <xsl:otherwise><xsl:text>Home </xsl:text></xsl:otherwise>
                </xsl:choose>
                <xsl:value-of select="."  /><xsl:text  >, </xsl:text>
            </xsl:for-each>

            <xsl:if test="EMAIL">
                <xsl:for-each select="EMAIL">
                    <xsl:choose>
                        <xsl:when test="@TYPE">
                            <xsl:value-of select="@TYPE" /><xsl:text  > </xsl:text> 
                        </xsl:when>
                        <xsl:otherwise><xsl:text  >Personal </xsl:text></xsl:otherwise>
                    </xsl:choose>
                    <xsl:value-of select="."  /><xsl:text  >, </xsl:text>
                </xsl:for-each>
            </xsl:if>
            <xsl:text>&#10;&#13;</xsl:text>
        </xsl:for-each>
    </xsl:template>
    <xsl:template match="text()|@*">
        <xsl:text>-</xsl:text>
    </xsl:template>

</xsl:stylesheet>

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X