XSLT – 从模板中删除空格

我正在使用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>

</xsl:text> </xsl:for-each> </xsl:template> </xsl:stylesheet> 

在XSLT中,空白是默认保存的,因为它可以是相关的数据。

在输出中防止不需要的空白的最好方法不是首先创build它。 不要做:

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

因为从处理器的angular度来说这就是"\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··" (在<xml>
  2. "·value·"
  3. \n" (在</xml>之前)

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

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

所有文本节点现在都是静音的,并且文本输出必须被明确地创build:

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

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

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

但要小心,因为函数规范了在string中find的任何空格,例如"·value··1·"将变成"value·1"

此外,您可以使用<xsl:strip-space><xsl:preserve-space>元素,尽pipe通常这不是必需的(并且我个人更喜欢显式的空白处理)。

默认情况下,XSLT模板具有<xsl:preserve-space>集合,这将在输出中保留空白。 您可以添加<xsl:strip-space elements="*">来告诉它在哪里删除空格。

您可能还需要包含一个normalize-space指令,如下所示:

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

这里是一个W3学校保留/剥离空间的例子 。

至于删除标签,但保留单独的行,我尝试了以下的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> 

使用replace函数XSLT 2.0使这个微不足道:

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

其他人已经指出了一般问题。 你的样式表的特定之一是你忘记了<xsl:text>的逗号:

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

这使得每个逗号之后的空格有意义,所以它在输出中结束。 如果将每个逗号包装在<xsl:text> ,问题就会消失。

另外,摆脱disable-output-escaping 。 这里没有做任何事情,因为你不输出XML。

将一个模板添加到您的xslt中

 <xsl:template match="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> 

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

使用缩进属性系统进行格式化时,会添加这些额外的空白空格。

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

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