我正在尝试使用XSLT将XML文件转换为dokuwiki使用的标记。这实际上在某种程度上起作用,但XSL文件中的缩进被插入到结果中。目前,我有两个选择:完全放弃这个XSLT,找到另一种从XML转换为dokuwiki标记的方法,或者从XSL文件中删除大约95%的空白,使其难以理解并成为维护的噩梦。
有没有办法在XSL文件中保留缩进而不将所有空格都传递给最终文档?
背景:我正在将静态HTML页面中的autodoc工具迁移到dokuwiki,因此,只要应用程序团队遇到编写不当的代码,应用程序团队就可以进一步记录服务器团队开发的API。逻辑是为autodoc工具留出每个页面的一部分,并允许在该块之外的任何地方进行注释。我正在使用XSLT,因为我们已经有了从XML转换为XHTML的XSL文件,而且我认为重写XSL比从头开始编写自己的解决方案要快。
编辑:啊,对,愚蠢我,我忽略了缩进属性。 (其他背景说明:我是XSLT的新手。)另一方面,我仍然需要处理新行。 Dokuwiki使用管道来区分表列,这意味着表行中的所有数据必须在一行上。有没有办法抑制输出的换行(只是偶尔),所以我可以在一个有点可读的区域为每个表格单元做一些相当复杂的逻辑?
答案 0 :(得分:77)
在XSLT转换结果中获取不需要的空格有三个原因:
我将谈论这三个因素,因为很难说出空白来自何处,所以你可能需要使用几种策略。
要解决源文档中节点之间的空白,应使用<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" />
elements
上的<xsl:preserve-space>
属性基本上应列出文档中包含混合内容的所有元素。
除此之外:使用
<xsl:strip-space>
还可以减少内存中源代码树的大小,并使样式表更有效率,因此即使您没有此类空白问题,也值得这样做。
要解决源文档中节点中出现的空白,应使用normalize-space()
。例如,如果您有:
<dt>
a definition
</dt>
并且您可以确定<dt>
元素不会包含您想要执行某些操作的元素,然后您可以执行以下操作:
<xsl:template match="dt">
...
<xsl:value-of select="normalize-space(.)" />
...
</xsl:template>
前导和尾随空格将从<dt>
元素的值中删除,您将获得字符串"a definition"
。
要解决来自样式表的空格,也就是您遇到的那个空格,就是在这样的模板中有文本时:
<xsl:template match="name">
Name:
<xsl:value-of select="." />
</xsl:template>
XSLT样式表的解析方式与它们处理的源文档相同,因此上述XSLT被解释为一个树,其中包含<xsl:template>
元素,其match
属性的第一个子元素是文本节点,其第二个子元素是<xsl:value-of>
元素,具有select
属性。文本节点具有前导和尾随空格(包括换行符);因为它是样式表中的文字文本,所以它会被字面上复制到结果中,并带有所有前导和尾随空格。
但是,XSLT样式表中的一些空格会被自动剥离,即节点之间的空格。您的结果中没有换行符,因为<xsl:value-of>
与<xsl:template>
的结束之间存在换行符。
要仅获得结果中所需的文本,请使用<xsl:text>
元素,如下所示:
<xsl:template match="name">
<xsl:text>Name: </xsl:text>
<xsl:value-of select="." />
</xsl:template>
XSLT处理器将忽略节点之间出现的换行符和缩进,并仅输出<xsl:text>
元素中的文本。
答案 1 :(得分:4)
你在输出标签中使用indent =“no”吗?
<xsl:output method="text" indent="no" />
此外,如果您使用的是xsl:value-of,则可以使用disable-output-escaping =“yes”来帮助解决一些空白问题。
答案 2 :(得分:3)
@JeniT的答案很棒,我只是想指出一个管理空白的技巧。我不确定这是最好的方式(甚至是好方法),但它现在对我有用。
(“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'技巧在包含至少一个非空白字符的文本节点之前有效,因为它扩展为:
<xsl:template match="/">
<xsl:text></xsl:text>Flush left, despite the indentation.<xsl:text>
</xsl:text>
由于rules for stripping whitespace表示只删除空白文本节点,因此&lt; xsl:template&gt;之间的换行符和缩进符号不同。和&lt; xsl:text&gt;被剥夺(好)。由于规则说保留了至少包含一个空白字符的文本节点,因此包含" This line will be output indented two spaces."
的隐式文本节点保持其前导空格(但我想这也取决于strip / preserve / normalize的设置)。 “&amp; n;”在行的末尾插入换行符,但它也确保忽略任何后续空格,因为它出现在两个节点之间。
我遇到的麻烦是当我想输出一个以&lt; xsl:value-of&gt;开头的缩进行。在那种情况下,“&amp; e;”将无济于事,因为缩进空白不会“附加”到任何非空白字符。所以对于那些情况,我使用“&amp; s2;”或“&amp; s4;”,取决于我想要多少缩进。
这是一个丑陋的黑客,我敢肯定,但至少我没有详细的“&lt; xsl:text&gt;”乱丢乱我的XSLT的标签,至少我仍然可以缩进XSLT本身,因此它清晰可辨。我觉得我在滥用XSLT是因为它不是为文本处理而设计的,这是我能做的最好的事情。
修改强> 在回应评论时,这就是没有“宏”的情况:
<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列(否则你得到输出文件中不需要的空格。)
答案 3 :(得分:0)
关于对新行的编辑,您可以使用此模板以递归方式替换另一个字符串中的一个字符串,并将其用于换行符:
<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>
调用它如下(此示例用空格替换$ some.string变量中的换行符):
<xsl:call-template name="replace.string.section">
<xsl:with-param name="in.string" select="$some.string"/>
<xsl:with-param name="in.characters" select="'
'"/>
<xsl:with-param name="out.characters" select="' '"/>
</xsl:call-template>