XSLT-Usuń białe znaki z szablonu

Używam XML do przechowywania małej listy kontaktów i próbuję napisać szablon XSL, który przekształci go w plik CSV. Problem, który mam, to białe znaki na wyjściu.

Wyjście:

Friend, John, Smith, Home,
        123 test,
       Sebastopol,
       California,
       12345,
     Home 1-800-123-4567, Personal [email protected]

Wciąłem / rozmieściłem zarówno źródłowy plik XML, jak i powiązany z nim szablon XSL, aby ułatwić czytanie i rozwijanie, ale cała ta dodatkowa Biała spacja dostaje się do wyjścia. Sam XML nie ma dodatkowych białych spacji wewnątrz węzłów, tuż poza nimi do formatowania, a to samo dotyczy XSLT.

Aby plik CSV był poprawny, każdy wpis musi znajdować się w jego własnej linii, a nie w podziale. Poza usunięciem wszystkich dodatkowych białych spacji z XML i XSLT (co czyni je tylko jedną długą linijką kodu), czy istnieje inny sposób na pozbycie się białych spacji na wyjściu?

Edytuj: Oto mała próbka XML:

<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>[email protected]</EMAIL>
        <RELATION>Friend</RELATION>
    </LISTING>
</PHONEBOOK>

A oto 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>
 38
Author: Robert DeBoer, 2009-09-24

7 answers

W XSLT, Biała spacja jest domyślnie zachowana, ponieważ bardzo dobrze mogą to być istotne dane.

Najlepszym sposobem zapobiegania niechcianym białym spacjom na wyjściu jest nie tworzenie go w pierwszej kolejności. Nie rób:

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

Ponieważ to jest "\n··foo\n", z punktu widzenia procesora. Raczej do

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

Biała spacja w arkuszu stylów jest ignorowana tak długo, jak występuje tylko pomiędzy elementami XML. Mówiąc najprościej: nigdy nie używaj "nagiego" tekstu w dowolnym miejscu kodu XSLT, zawsze dołącz go do element.

Również, używając nieokreślonego:

<xsl:apply-templates />

Jest problematyczne, ponieważ domyślna reguła XSLT dla węzłów tekstowych mówi "skopiuj je na wyjście". Dotyczy to również węzłów "white-space-only". Na przykład:

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

Zawiera trzy węzły tekstowe:

  1. "\n··" (zaraz po <xml>)
  2. "·value·"
  3. "\n" (tuż przed </xml>)

Aby uniknąć, że #1 i #3 wkradają się do wyjścia (co jest najczęstszym powodem niechcianych spacje), możesz nadpisać domyślną regułę dla węzłów tekstowych deklarując pusty szablon:

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

Wszystkie węzły tekstowe są teraz wyciszone, a wyjście tekstowe musi być utworzone jawnie:

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

Aby usunąć białe spacje z wartości, możesz użyć normalize-space() funkcji XSLT:

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

Ale ostrożnie, ponieważ funkcja normalizuje każdą białą przestrzeń znajdującą się w łańcuchu, np. "·value··1·" stałaby się "value·1".

Dodatkowo można użyć elementów <xsl:strip-space> i <xsl:preserve-space>, choć zazwyczaj nie jest to konieczne (i osobiście wolę jawną obsługę białych spacji, jak wskazano powyżej).

 80
Author: Tomalak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-24 11:26:44

Domyślnie szablony XSLT mają ustawione <xsl:preserve-space>, które zachowają białe znaki w wynikach. Możesz dodać <xsl:strip-space elements="*">, aby powiedzieć gdzie usunąć białe znaki.

Może być również konieczne dołączenie dyrektywy normalize-space, w ten sposób:

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

Oto przykład zachowania / usunięcia przestrzeni ze szkół W3.

 8
Author: Noah Heldman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-30 19:13:36

Jeśli chodzi o usuwanie tabulatorów, ale zachowując oddzielne linie, wypróbowałem następujące podejście XSLT 1.0 i działa dość dobrze. Korzystanie z wersji 1.0 lub 2.0 w dużej mierze zależy od używanej platformy. Wygląda na to, że technologia. NET jest nadal zależna od XSLT 1.0, a więc jesteś ograniczony do bardzo niechlujnych szablonów(patrz poniżej). Jeśli używasz Javy lub czegoś innego, zapoznaj się z dużo czystszym podejściem XSLT 2.0 wymienionym na samym dole.

Te przykłady mają na celu być rozszerzone przez Ciebie w celu spełnienia Twoich specyficznych potrzeb. Używam tabs tutaj jako przykład, ale to powinno być na tyle ogólne, aby być rozszerzalne.

XML:

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

                dsalkfjdsaflkj

            lkasdfjlsdkfaj
</text>

...i szablon XSLT 1.0 (wymagany, jeśli używasz. NET):

<?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 czyni to trywialnym za pomocą funkcji replace:

<?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>
 2
Author: David Andres,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-24 00:04:58

Inni już zwrócili uwagę na ogólny problem. Specyficznym dla Twojego arkusza stylów jest to, że zapomniałeś <xsl:text> o przecinkach:

   <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" />,

To sprawia, że spacje po przecinku są znaczące, a więc kończy się na wyjściu. Jeśli zawijasz każdy przecinek w <xsl:text>, problem znika.

Pozbądź się też tego disable-output-escaping. Tutaj nic nie robi, ponieważ nie wysyłasz XML.

 1
Author: Pavel Minaev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-24 16:12:55

Dodaj jeden szablon do swojego xslt

<xsl:template match="text()"/>
 1
Author: Nick Groznykh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-19 14:36:02

Moja poprzednia odpowiedź jest błędna, wszystkie przecinki muszą być wypisane przez tag 'text'

<?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>
 1
Author: Nick Groznykh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-26 09:37:32

Zmodyfikuj kod, którego użyliśmy do formatowania surowego pliku xml, usuwając poniższe linie, usunie dodatkowe puste białe spacje dodane w eksportowanym Excelu.

Podczas formatowania z wciętą właściwością system dodaje te dodatkowe puste białe spacje.

Skomentuj wiersze związane z formatowaniem xml jak poniżej i spróbuj.

xmlWriter.Formatting = System.Xml.Formatting.Indented;
 0
Author: Tejas Sawant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-04 05:20:52