2

我正在编写一个XSLT过滤器,它读取一个 XML 文件,并生成一个较短的 XML 文件,其中删除了一些选定的元素(及其所有子元素)。

到目前为止,我的过滤器给我的输出是有效的、格式良好的 XML,但它在删除元素的位置有空行。形式上,我认为删除元素之前的文本节点仍然存在,导致空行。我想删除这个空白行,保留所有其他缩进。我怎样才能做到这一点?

我的 XSLT 过滤器的简化版本是:

<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" omit-xml-declaration="no" indent="yes" encoding="utf-8" />

    <xsl:template match="node() | @*">
        <xsl:copy>
            <xsl:apply-templates select="node() | @*" />
        </xsl:copy>
    </xsl:template>

<xsl:template match="root/maybe[remove]" />

</xsl:stylesheet>

我的输入 XML 文件的一个非常简化的版本是:

<?xml version="1.0" encoding="utf-8" ?>
<root>
      <maybe><keep /></maybe>
   <maybe><remove/></maybe>
</root>

是的,缩进是非标准的。我试图表明我希望过滤器留下它找到的缩进,除了它删除的元素。这让我可以使用传统的diff确认结果。

我现在得到的输出(在 MacOS X 10.10 上使用xsltprocfrom libxslt):

<?xml version="1.0" encoding="utf-8"?>
<root>
       <maybe><keep/></maybe>

</root>

<keep/>和之间的空白行</root>是我要消除的。

现在,在 SO 的其他地方,相关问题XSLT:如何防止 XSLT 代码在输出 xml 中生成冗余空白删除 XSLT 中的空白行建议将xsl:strip-space添加到 XSLT 过滤器:

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

当我尝试这样做时,输出文件不再有空行,但它现在具有与原始文件不同的缩进:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <maybe>
    <keep/>
  </maybe>
</root>

(请注意,<maybe>and<keep/></maybe>现在位于不同的行,并且缩进不同。)

那么,是否有一个 XSLT 元素会连同元素一起删除空行,但保留其他缩进和换行符?

此外,我的真实文件来自GnuCash,并且具有更复杂的结构。我真正的 XSLT 过滤器有一个更复杂的匹配表达式。因此,首选不需要我重复匹配表达式的 XSLT 元素。

<xsl:template match='gnc-v2//gnc:account[@version="2.0.0"]/act:slots/
        slot[slot:key/text()="import-map-bayes"]/slot:value[@type="frame"]/
        slot/slot:value[@type="frame"]/slot[starts-with(slot:key/text(),
            "Assets, Business, CAD:"
    )]' />

此外,一个相关的问题Removing extra blank lines with XSLT, without using indentation没有答案。那里没有洞察力。

我正在使用 XSLT 1.0,因为这是我的工具所支持的。XSLT 2.0 是否为这个问题提供了更好的答案?

更新:稍微简化了匹配模式,提到了 XSLT 1 vs 2。

4

3 回答 3

3

只需添加此模板

  <xsl:template match="text()[following-sibling::node()[1][self::maybe[remove]]]" />

完整的样式表变为

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" omit-xml-declaration="yes"/>

  <xsl:template match="node() | @*">
     <xsl:copy>
       <xsl:apply-templates select="node() | @*" />
     </xsl:copy>
  </xsl:template>

  <xsl:template match="root/maybe[remove]" />
  <xsl:template match="text()[following-sibling::node()[1][self::maybe[remove]]]" />
</xsl:stylesheet>

请注意:我已经删除了该indent="yes"属性,因为这肯定会弄乱(标准化缩进)输出。

应用于提供的 XML 文档时

<root>
      <maybe><keep /></maybe>
   <maybe><remove/></maybe>
</root>

产生了想要的结果

<root>
      <maybe><keep/></maybe>
</root>

如果您还想删除相邻的前面的注释和/或处理指令,那么要添加的模板是:

      <xsl:template match=
          "node()[not(self::*)][following-sibling::*[1][self::maybe[remove]]]" />
于 2016-02-21T22:57:54.260 回答
1

如果您真的想在模式中使用变量,那么我认为您需要迁移到当前由 EXSELT 或 Saxon 9.6 或 9.7 的商业版支持的 XSLT 3.0。

使用 EXSLT,我使用变量和键尝试了以下操作:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">

    <xsl:key name="version" match="foo" use="@version"/>
    <xsl:key name="item" match="foo/bar/item" use="@key"/>

    <xsl:variable name="vers2" select="key('version', '2.0.0')"/>

    <xsl:variable name="k1" select="key('item', 'k1', $vers2)"/>

    <xsl:variable name="data1" select="$k1/data[starts-with(., 'abc')]"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="$data1| 
                         $k1/text()[not(normalize-space())][some $d in $data1 satisfies ($d is following-sibling::node()[1])]"/>

</xsl:transform>

它转换表单的输入样本

<root>
  <foo version="2.0.0">
    <bar>
      <item key="k1">
        <data>abcdefg</data>
        <data>1234567</data>
      </item>
      <item key="k1">
        <data>1234567</data>
        <data>abcdefg</data>
      </item>
      <item key="k2">
        <data>1234567</data>
        <data>abcdefg</data>
      </item>
      <item key="k1">
        <data>foo</data>
        <data>abcdefg</data>
        <data>abcjjjj</data>
        <data>bar</data>
        <data>abcllll</data>
      </item>
    </bar>
  </foo>
  <foo version="1.0.0">
    <bar>
      <item key="k1">
        <data>abcdefg</data>
        <data>1234567</data>
      </item>
      <item key="k1">
        <data>1234567</data>
        <data>abcdefg</data>
      </item>
      <item key="k2">
        <data>1234567</data>
        <data>abcdefg</data>
      </item>
    </bar>
  </foo>
</root>

进入

<root>
  <foo version="2.0.0">
    <bar>
      <item key="k1">
        <data>1234567</data>
      </item>
      <item key="k1">
        <data>1234567</data>
      </item>
      <item key="k2">
        <data>1234567</data>
        <data>abcdefg</data>
      </item>
      <item key="k1">
        <data>foo</data>
        <data>bar</data>
      </item>
    </bar>
  </foo>
  <foo version="1.0.0">
    <bar>
      <item key="k1">
        <data>abcdefg</data>
        <data>1234567</data>
      </item>
      <item key="k1">
        <data>1234567</data>
        <data>abcdefg</data>
      </item>
      <item key="k2">
        <data>1234567</data>
        <data>abcdefg</data>
      </item>
    </bar>
  </foo>
</root>

Saxon 9.6/9.7 的商业版(EE 和 PE)也运行上述代码并产生与 Exselt 相同的结果。

至于将 XSLT 3.0 与您的真实示例一起使用,因为它们似乎在名称空间中具有元素,使用xpath-default-namespace可以简化 XSLT 2.0 或 3.0 中的内容以具有短匹配模式。

于 2016-02-16T17:55:15.003 回答
0

这个 XSLT 过滤器给出了想要的结果:

<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" omit-xml-declaration="no" indent="yes" encoding="utf-8" />

    <xsl:template match="node() | @*">
        <xsl:copy>
            <xsl:apply-templates select="node() | @*" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="root/maybe[./remove]" />
    <xsl:template match="root/text()[following-sibling::maybe[1]/remove]" />

</xsl:stylesheet>

结果:

<?xml version="1.0" encoding="utf-8"?>
<root>
       <maybe><keep/></maybe>
</root>

但是,随着匹配表达式变得又长又复杂,这种方法变得非常难看。这两个模板元素在匹配模式中有很多冗余。然而,这种冗余是无济于事的。我们不能将公共部分放在变量中。XSLT 1.0 规范说:“匹配属性的值包含变量引用是错误的。”

肯定有人可以做得更好吗?

于 2016-02-15T10:49:01.377 回答