【问题标题】:XSLT, sorting, and position()XSLT、排序和位置()
【发布时间】:2012-05-27 11:18:16
【问题描述】:

开始使用 XSLT,这很有趣,我喜欢校长,尽管需要一点时间来适应。现在...我已经遇到了一个我想知道的问题,也许有人可以启发我。

我有一组具有优先级的框。优先级较低的框首先出现。这很好用。但是,我想将我的盒子成行放置,并将第 1 行和第 3 行标记为奇数,将第 2 行标记为偶数(我预计在实际项目中会有更多行。)

因此,我认为我应该能够使用 fn:position() 函数。不幸的是,返回的位置是原始节点之一,而不是结果排序节点。有没有办法解决这个问题?

有一个示例 XSLT 暴露了这个问题:

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="body">
    <xsl:apply-templates>
      <xsl:sort select="@priority" data-type="number"/>
    </xsl:apply-templates>
  </xsl:template>
  <xsl:template match="body/box">
<div class="box">
Box[<xsl:value-of select="count(preceding-sibling::box) + 1" />,<xsl:value-of select="position()"/>] = (<xsl:value-of select="@priority"/>)
  <xsl:copy-of select="title"/></div>
  </xsl:template>
</xsl:stylesheet>

还有一个输入示例。应该先出现两个,然后出现三个,最后出现一个。

<?xml version="1.0"?>
<body>
  <box priority="103">
    <title>This is box ONE</title>
  </box>
  <box priority="1">
    <title>This is box TWO</title>
  </box>
  <box priority="12">
    <title>This is box THREE</title>
  </box>
</body>

但我希望 position() 为 1、2 和 3...但这是输出:

<div class="box">
Box[2,4] = (1)
  <title>This is box TWO</title>
</div>
<div class="box">
Box[3,6] = (12)
  <title>This is box THREE</title>
</div>
<div class="box">
Box[1,2] = (103)
  <title>This is box ONE</title>
</div>

Box 方括号内的第二个数字是 position()。我预计是 1、2、3。但正如我们所见,我们得到 4、6、2,这是原始文档中节点的位置(我不太确定为什么它是 x2,但当我用xsl:for-each 标签,它是 2、3 和 1。)

我用 xmlpatterns (Qt 4.7) 和 xsltproc (libxml2, 这应该使用 1.0 版本,代码兼容 1.0) 进行测试,并且都返回相同的数字。

那么...这是 XSLT 的限制,还是这两个 XSLT 实现中的错误?!

2012 年 5 月 27 日更新

已确定 QXmlQuery (xmlpatterns) 在该特定情况下是一个损坏的解析器。 position() 必须使用类似于 count(preceding-sibling::box) + 1 的东西来计算,而不是在运行的 for-each 或模板序列中使用正确的索引。

【问题讨论】:

  • 2012 年 12 月更新,我现在在 Ubuntu 12.04 上使用 Qt 4.8,效果更好。还不完美,但我通常可以得到我期望的结果。

标签: xslt sorting position


【解决方案1】:

你做得很好

<xsl:apply-templates>
  <xsl:sort select="@priority" data-type="number"/>
</xsl:apply-templates>

是doing的缩写

<xsl:apply-templates select="node()">
  <xsl:sort select="@priority" data-type="number"/>
</xsl:apply-templates>

它处理所有类型的子节点,包括您似乎感兴趣的元素节点以及元素之间的空白文本节点。所以使用

<xsl:apply-templates select="box">
  <xsl:sort select="@priority" data-type="number"/>
</xsl:apply-templates>

你至少应该将 position() 设为 1,2,3,如你所愿。

不管怎样,我用样式表在 Windows 上测试了 xsltproc

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="body">
    <xsl:apply-templates select="box">
      <xsl:sort select="@priority" data-type="number"/>
    </xsl:apply-templates>
  </xsl:template>
  <xsl:template match="body/box">
<div class="box">
Box[<xsl:value-of select="count(preceding-sibling::box) + 1" />,<xsl:value-of select="position()"/>] = (<xsl:value-of select="@priority"/>)
  <xsl:copy-of select="title"/></div>
  </xsl:template>
</xsl:stylesheet>

反对您发布的输入,结果是

compilation error: file test2012052702.xsl line 2 element stylesheet
xsl:version: only 1.0 features are supported
<?xml version="1.0"?>
<div class="box">
Box[2,1] = (1)
  <title>This is box TWO</title></div><div class="box">
Box[3,2] = (12)
  <title>This is box THREE</title></div><div class="box">
Box[1,3] = (103)
  <title>This is box ONE</title></div>

所以我认为这个位置是正确的。

【讨论】:

  • 嗯...刚试过,我在使用 select="node()" 时得到了完全相同的结果。但是, select="box" 按预期工作,将选择限制为框标签。但是 position() 仍然是 2, 3, 1 而不是 1, 2, 3...
  • 当我仅在 box 元素上使用我对 apply-templates 的建议更改时,我无法重现 xsltproc 和 Saxon 9 的问题,所以我不确定你为什么获得这个职位你显示的那个顺序。
  • 好的!没错,xsltproc 可以正常工作。 xmlpatterns 仍然说 2, 3, 1...
  • 那么 xmlpatterns 如果它应该实现 XSLT 1.0 或 2.0 规范,它就有一个 bug。
  • 马丁,是的,确实如此。他们在文档中的某处说他们的合规率约为 54%。但我没想到这么基本的东西会这么错误。
【解决方案2】:

无法重现

我使用 Saxon 9.1.07 运行您的转换,结果是:

<div class="box">
      Box[2,5] = (1)
      <title>This is box TWO</title></div><div class="box">
      Box[3,6] = (12)
      <title>This is box THREE</title></div><div class="box">
      Box[1,7] = (103)
      <title>This is box ONE</title></div>

正如我们所见,位置是正确的(仍然不是 1,2,3 - 因为也存在纯空白节点)。

XQSharp (XmlPrime) 产生相同的结果。

AltovaXML2009 (XML-SPY) 甚至可以生成这个

<?xml version="1.0" encoding="UTF-8"?><div class="box">
    Box[2,1] = (1)
      <title>This is box TWO</title></div><div class="box">
    Box[3,2] = (12)
      <title>This is box THREE</title></div><div class="box">
    Box[1,3] = (103)
      <title>This is box ONE</title></div>

这意味着它使用了一个 XML 解析器,该解析器去除了纯空白文本节点。

此转换的改进版本将排除纯空白节点

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="body">
        <xsl:apply-templates>
          <xsl:sort select="@priority" data-type="number"/>
        </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="box">
    <div class="box">
      Box[<xsl:value-of select="count(preceding-sibling::box) + 1" />
      <xsl:text>,</xsl:text>
      <xsl:value-of select="position()"/>
      <xsl:text>] = (</xsl:text>
      <xsl:value-of select="@priority"/>)
      <xsl:copy-of select="title"/></div>
 </xsl:template>
</xsl:stylesheet>

当对提供的 XML 文档应用此转换时:

<body>
    <box priority="103">
        <title>This is box ONE</title>
    </box>
    <box priority="1">
        <title>This is box TWO</title>
    </box>
    <box priority="12">
        <title>This is box THREE</title>
    </box>
</body>

产生想要的正确结果

<div class="box">
      Box[2,1] = (1)
      <title>This is box TWO</title>
</div>
<div class="box">
      Box[3,2] = (12)
      <title>This is box THREE</title>
</div>
<div class="box">
      Box[1,3] = (103)
      <title>This is box ONE</title>
</div>

注意事项xsl:strip-space 用于排除仅包含空白的节点,甚至不被解析。

结论:报告的结果要么是由于使用了有问题的 XSLT 处理器,要么是由于执行了与提供的不同的转换。

【讨论】:

  • 这就是我使用两个不同的解析器进行测试的原因。我在想 libxml2 解析器会更像你所期望的(更接近 XSLT 参考所说的。)马丁的修复(另一个答案)实际上修复了 xsltproc(libxml2 解析器)但 xmlpattern(Qt 4.7 版本)仍然破碎的。这样解析器就不能正确地重新计算 position() !烦人...感谢您提供详细信息。
猜你喜欢
  • 1970-01-01
  • 2017-10-31
  • 2021-05-31
  • 1970-01-01
  • 2015-04-26
  • 2022-08-20
  • 2010-12-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多