【问题标题】:XSLT - remove whitespace from templateXSLT - 从模板中删除空格
【发布时间】:2009-09-23 23:09:31
【问题描述】:

我正在使用 XML 存储一个小型联系人列表,并尝试编写一个 XSL 模板,将其转换为 CSV 文件。我遇到的问题是输出中有空格。

输出:

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

我已经对源 XML 文件和相关的 XSL 模板进行了缩进/分隔,以使其更易于阅读和开发,但所有额外的空白都会将其自身放入输出中。 XML 本身在节点内部没有额外的空格,只是在它们外部用于格式化,XSLT 也是如此。

为了使 CSV 文件有效,每个条目都需要在自己的行上,而不是分开。除了从 XML 和 XSLT 中去除所有额外的空白(使它们成为一长行代码)之外,还有其他方法可以消除输出中的空白吗?

编辑: 这是一个小的 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>john.smith@gmail.com</EMAIL>
        <RELATION>Friend</RELATION>
    </LISTING>
</PHONEBOOK>

这里是 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>

【问题讨论】:

  • 为什么在每个&lt;xsl:text&gt; 上都使用disable-output-escaping&lt;xsl:output method='text'&gt; 不需要它。来自 XSLT 1.0 规范:“文本输出方法忽略了 disable-output-escaping 属性,因为它不执行任何输出转义。”

标签: xml xslt


【解决方案1】:

在 XSLT 中,默认情况下会保留空白,因为它很可能是相关数据。

防止输出中出现不需要的空白的最佳方法是不要一开始就创建它。不要这样做:

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

因为从处理器的角度来看,这是"\n··foo\n"。宁可做

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

只要样式表中的空白仅出现在 XML 元素之间,它就会被忽略。简单地说:永远不要在 XSLT 代码中的任何地方使用“裸”文本,始终将其包含在一个元素中。

另外,使用不特定的:

<xsl:apply-templates />

是有问题的,因为文本节点的默认 XSLT 规则说“将它们复制到输出”。这也适用于“仅空白”节点。例如:

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

包含三个文本节点:

  1. "\n··"(紧跟在&lt;xml&gt;之后)
  2. "·value·"
  3. \n"(就在&lt;/xml&gt;之前)

为避免 #1 和 #3 潜入输出(这是不需要空格的最常见原因),您可以通过声明一个空模板来覆盖文本节点的默认规则:

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

现在所有文本节点都已静音,并且必须显式创建文本输出:

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

要从值中删除空格,您可以使用normalize-space() XSLT 函数:

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

但要小心,因为该函数会规范化字符串中的任何空白,例如"·value··1·" 将变为 "value·1"

此外,您还可以使用&lt;xsl:strip-space&gt;&lt;xsl:preserve-space&gt; 元素,尽管通常这不是必需的(而且我个人更喜欢如上所述的显式空白处理)。

【讨论】:

    【解决方案2】:

    默认情况下,XSLT 模板设置了&lt;xsl:preserve-space&gt;,这将在您的输出中保留空格。您可以添加&lt;xsl:strip-space elements="*"&gt; 告诉它 在哪里删除空格。

    您可能还需要包含规范化空间指令,如下所示:

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

    这是example for preserve/strip space from W3 Schools

    【讨论】:

    【解决方案3】:

    至于删除选项卡但保留单独的行,我尝试了以下 XSLT 1.0 方法,并且效果很好。您对 1.0 或 2.0 版本的使用很大程度上取决于您使用的平台。看起来 .NET 技术仍然依赖于 XSLT 1.0,因此您只能使用极其混乱的模板(见下文)。如果您使用 Java 或其他东西,请参考最底部列出的更简洁的 XSLT 2.0 方法。

    这些示例旨在由您扩展以满足您的特定需求。我在这里使用选项卡作为示例,但这应该足够通用以进行扩展。

    XML:

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

    ...和 ​​XSLT 1.0 模板(如果您使用 .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 使用 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>
    

    【讨论】:

      【解决方案4】:

      其他人已经指出了一般问题。您的样式表的具体一个是您忘记了&lt;xsl:text&gt; 逗号:

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

      这使得每个逗号后面的空格都很重要,因此它最终出现在输出中。如果您将每个逗号包含在&lt;xsl:text&gt; 中,问题就会消失。

      另外,摆脱那个disable-output-escaping。它在这里没有任何作用,因为您没有输出 XML。

      【讨论】:

        【解决方案5】:

        我之前的回答是错误的,所有的逗号必须通过标签'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>
        

        【讨论】:

          【解决方案6】:

          此答案可能无法直接回答问题。但是一个通用的方法可以解决这个问题。创建模板规则:

          <xsl:template name="strip-space">
              <xsl:param name="data"/>
              <xsl:value-of select="normalize-space($data)"/>
          </xsl:template>
          

          现在调用它来删除多余的空格:

          <xsl:template match="my-element">
              <xsl:call-template name="strip-space">
                  <xsl:with-param name="data">
                      <xsl:apply-templates/>
                  </xsl:with-param>
              </xsl:call-template>
          </xsl:template>
          

          例如,考虑下面的 XML 片段:

          <?xml version="1.0" encoding="UTF-8"?>
          <test>
              <my-element>
                  <e1>some text</e1> <e2>some other text</e2> <e3>some other text</e3>
              </my-element>
          </test>
          

          如果有人喜欢将其转换为以下文本:

          {test{my-element{e1some text} {e2some other text} {e3some other 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="/">
                  <xsl:apply-templates mode="t1"/>
                  <xsl:text>&#xa;</xsl:text>
                  <xsl:apply-templates mode="t2"/>
              </xsl:template>
          
              <xsl:template match="*" mode="t1">
                  <xsl:text>{</xsl:text>
                  <xsl:value-of select="local-name()"/>
                  <xsl:call-template name="strip-space">
                      <xsl:with-param name="data">
                          <xsl:apply-templates mode="t1"/>
                      </xsl:with-param>
                  </xsl:call-template>
                  <xsl:text>}</xsl:text>
              </xsl:template>
          
              <xsl:template match="*" mode="t2">
                  <xsl:text>{</xsl:text>
                  <xsl:value-of select="local-name()"/>
                  <xsl:value-of select="."/>
                  <xsl:text>}</xsl:text>
              </xsl:template>
          
              <xsl:template name="strip-space">
                  <xsl:param name="data"/>
                  <xsl:value-of select="normalize-space($data)"/>
              </xsl:template>
          
          </xsl:stylesheet>
          

          应用样式表后,会产生:

          {test{my-element{e1some text} {e2some other text} {e3some other text}}}
          
          {test
          
                  some text some other text some other text
          
          }
          

          输出描述了@mode="t1"&lt;xsl:value-of select="."/&gt; 方法)与@mode="t2"xsl:call-template 方法)的不同之处。希望这对某人有所帮助。

          【讨论】:

            【解决方案7】:

            在你的 xslt 中添加一个模板

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

            【讨论】:

            • 为什么?如何?谢谢您的回答,但请始终提供更多信息。
            【解决方案8】:

            通过删除以下行来修改我们用于格式化原始 xml 文件的代码将删除在导出的 excel 中添加的额外空白。

            虽然使用缩进属性系统进行格式化会添加那些额外的空白空格。

            与格式化 xml 相关的注释行如下行并尝试。

            xmlWriter.Formatting = System.Xml.Formatting.Indented;
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2012-04-08
              • 2015-07-17
              • 1970-01-01
              • 1970-01-01
              • 2018-07-14
              • 2016-06-16
              • 1970-01-01
              相关资源
              最近更新 更多