将XML转换为纯文本 – 我应该如何忽略/处理XSLT中的空白?

我正尝试使用XSLT将XML文件转换为dokuwiki使用的标记。 这实际上在某种程度上起作用,但是XSL文件中的缩进被插入到结果中。 目前,我有两种select:完全放弃这个XSLT,find另一种方法将XML转换成dokuwiki标记,或者从XSL文件中删除大约95%的空白,这使得它变得难以理解,并且成为维护的噩梦。

有没有办法在XSL文件中保留缩进而不将所有的空格都传递给最终文档?

背景:我将一个autodoc工具从静态HTML页面迁移到dokuwiki,所以只要应用程序团队运行到文档不完整的代码中,应用程序团队就可以进一步logging由服务器团队开发的API。 逻辑是将每个页面的一部分留给autodoc工具,并允许在这个块之外的任何地方进行注释。 我使用的是XSLT,因为我们已经有了从XML到XHTML的XSL文件,而且我认为重写XSL比从头开始自己的解决scheme要快。

编辑:啊,对,愚蠢的我,我忽略了缩进属性。 (其他背景说明:我是XSLT新手)另一方面,我仍然需要处理换行符。 Dokuwiki使用pipe道来区分表格列,这意味着表格行中的所有数据都必须在一行上。 有没有办法抑制输出新行(只是偶尔),所以我可以做一些相当复杂的逻辑为每个表单元格在一个可读的方法?

在XSLT转换的结果中获得不需要的空白有三个原因:

  1. 来自源文档中节点之间的空格
  2. 来自源文档中节点的空格
  3. 来自样式表的空白

我将要讨论所有这三个问题,因为很难确定空白是从哪里来的,所以你可能需要使用几种策略。

要解决源文档中节点之间的空白,应使用<xsl:strip-space>去除出现在两个节点之间的任何空白,然后使用<xsl:preserve-space>来保留重要的空白可能会出现在混合内容中。 例如,如果您的源文档如下所示:

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

那么你会想要忽略<ul><li>之间以及</li></ul>之间的空格,但不能保留<strong><em>之间的空格。元素,这重要的(否则你会得到“这是一个**重要的***点*”)。 要做到这一点使用

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

<xsl:preserve-space>上的elements属性应该基本列出文档中具有混合内容的所有元素。

另外:使用<xsl:strip-space>也可以减less内存中源代码树的大小,并且可以提高样式表的效率,所以即使没有这种types的空白问题,也是值得的。

要解决在源文档中的节点中出现的空白,应该使用normalize-space() 。 例如,如果您有:

 <dt> a definition </dt> 

你可以确定<dt>元素不会包含你想要做的任何元素,那么你可以这样做:

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

前导和尾随的空白符将从<dt>元素的值中删除,并且您将只获得string"a definition"

为了解决来自样式表的空白,或许是您遇到的样式表中的空白,就是在这样的模板中有文本:

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

XSLT样式表的parsing方式与它们处理的源文档相同,所以上面的XSLT被解释为一棵树,它包含一个match属性的<xsl:template>元素,其第一个子元素是文本节点,第二个子元素是具有select属性的<xsl:value-of>元素。 文本节点具有前导和尾随的空白(包括换行符); 因为它是样式表中的文本文本,所以它被逐字地复制到结果中,所有前导和尾随的空白。

但是XSLT样式表中的一些空白会被自动剥离,即节点之间的空白。 您的结果中不会出现换行符,因为<xsl:value-of><xsl:template>的closures之间存在换行符。

要只在结果中获得所需的文本,请使用<xsl:text>元素,如下所示:

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

XSLT处理器将忽略节点之间出现的换行符和缩进,只输出<xsl:text>元素中的<xsl:text>

你在输出标签中使用indent =“no”吗?

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

另外,如果您使用xsl:value-of,则可以使用disable-output-escaping =“yes”来解决一些空白问题。

@ JeniT的回答非常好,我只想指出一个pipe理空白的技巧。 我不确定这是最好的方式(甚至是一个好方法),但是现在对我来说很有用。

(“s”表示空格,“e”表示空格,“n”表示换行符。)

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

应用于:

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

输出:

 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 

“e”技巧在包含至less一个非空白字符的文本节点之前工作,因为它展开为:

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

由于剥离空白的规则说只有空白的文本节点被剥离,所以<xsl:template>和<xsl:text>之间的换行和缩进被剥离(好)。 由于规则说一个至less有一个空格字符的文本节点被保留,隐含的文本节点包含" This line will be output indented two spaces." 保持领先的空白(但我想这也取决于strip / preserve / normalize的设置)。 然后;” 在行的末尾插入一个换行符,但是它也可以确保忽略任何随后的空格,因为它出现在两个节点之间。

我遇到的麻烦是当我想输出一个以<xsl:value-of>开头的缩进行。 在这种情况下,“&e;” 将无济于事,因为缩进空格不是“附加”到任何非空白字符。 所以对于这些情况,我使用“&s2;” 或“&s4;”,取决于我想要多less缩进。

这是一个丑陋的黑客我敢肯定,但至less我没有乱扔我的XSLT的详细“<xsl:text>”标签,至less我仍然可以缩进XSLT本身,所以它是清晰的。 我觉得我滥用XSLT的东西不是专为(文本处理),这是我能做的最好的。


编辑:回应评论,这是没有“macros”的样子:

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

我认为这样就不太清楚看到预期的输出缩进,并且它将XSL本身的缩进缩小了,因为</xsl:text>结束标记必须出现在XSL文件的第1列(否则会得到不希望的空白在输出文件中)。

关于你对新行的编辑,你可以使用这个模板recursion地replace另一个string中的一个string,你可以用它来换行符:

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

如下调用它(这个例子用一个空格replace$ some.stringvariables中的换行符):

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