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

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

背景:我正在将静态HTML页面中的autodoc工具迁移到dokuwiki,因此,只要应用程序团队遇到编写不当的代码,应用程序团队就可以进一步记录服务器团队开发的API。逻辑是为autodoc工具留出每个页面的一部分,并允许在该块之外的任何地方进行注释。我正在使用XSLT,因为我们已经有了从XML转换为XHTML的XSL文件,而且我认为重写XSL比从头开始推出自己的解决方案更快。

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

有帮助吗?

解决方案

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

  1. 来自源文档中节点之间的空格
  2. 来自源文档中节点内的空格
  3. 来自样式表的空白
  4. 我将谈论这三个因素,因为很难说出空白来自何处,所以你可能需要使用几种策略。

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

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

    然后你会想要忽略<ul><li>之间以及</li></ul>之间的空格,这是不重要的,但要保留<strong>和<=之间的空格> 重要的元素(否则你会得到<!>“;这是一个**重要的***点* <!>”;)。为此,请使用

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

    <em>上的elements属性应基本列出文档中具有混合内容的所有元素。

      

    除此之外:使用normalize-space()还可以减少内存中源代码树的大小,并使样式表更有效,所以即使你没有这种空白问题也值得做。

    要解决源文档中节点中出现的空白,应使用<dt>。例如,如果您有:

    <dt>
      a definition
    </dt>
    

    并且您可以确定"a definition"元素不会包含您想要执行某些操作的任何元素,然后您可以执行以下操作:

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

    前导和尾随空格将从<xsl:template>元素的值中删除,您将获得字符串match

    要解决来自样式表的空格,也就是您遇到的那个空格,就是在这样的模板中有文本时:

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

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

    但是,XSLT样式表中的一些空格会被自动剥离,即节点之间的空格。您没有在结果中获得换行符,因为<=>和<=>的结束之间存在换行符。

    要仅获取结果中所需的文本,请使用<=>元素,如下所示:

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

    XSLT处理器将忽略节点之间出现的换行符和缩进,并仅输出<=>元素中的文本。

其他提示

您使用的是indent = <!>;否<!>在你的输出标签?

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

此外,如果你使用的是xsl:value-of,你可以使用disable-output-escaping = <!> quot; yes <!> quot;帮助解决一些空白问题。

@JeniT的答案很棒,我只是想指出一个管理空白的技巧。我不确定这是最好的方式(甚至是好方法),但它现在对我有用。

(<!> quot; s <!>; for space,<!> quot; e <!> quot; for empty,<!> quot; n <!> quot; for newline。)

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

由于剥离空白的规则表示只删除空白文本节点, <!> 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; <!> quot;或<!>“; <!> amp; s4; <!>”;取决于我想要多少缩进。

这是一个丑陋的黑客,我敢肯定,但至少我没有详细的<!>“<!> <<!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列(否则会得到不需要的空格)在输出文件中)。

关于对新行的编辑,您可以使用此模板以递归方式替换另一个字符串中的一个字符串,并将其用于换行符:

<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="'&#xA;'"/>
        <xsl:with-param name="out.characters" select="' '"/>
    </xsl:call-template>
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top