0

我目前正在使用 XSLT 2.0 来尝试处理大量数据。我的目标是为整个数据中特定节点的唯一出现调用特定模板。我不能使用“应用模板”,因为我有其他需要根据不同逻辑使用的模板。xml 的结构如下:

<root>
  <row>
     <date>date1</date>
     <child>
        <ID>001</ID>
     </child>
  </row>
  <row>
     <date>date2</date>
     <child>
        <ID>002</ID>
     </child>
  </row>
  <row>
     <date>date1</date>
     <child>
        <ID>002</ID>
     </child>
  </row>
  <row>
     <date>date3</date>
     <child>
        <ID>002</ID>
     </child>
  </row>
</root>

理想情况下,我会在这种情况下调用模板两次,因为有两个唯一的 ID 001 和 002,无论该 ID 上出现多少日期。我见过几个例子,人们要么使用 Keys 要么使用前面的兄弟方法;但是,我似乎永远无法正确使用语法。我尝试了以下方法:

被调用的模板:

<xsl:template name='doSomething'/>
   <xsl:value-of select='child/ID'/>
   <xsl:value-of select='date'/>
</template>

=====================

<xsl:for-each select='/root/row[not(child/ID = current()child/ID)]
   <xsl:call-template name='doSomething'/>
</xsl:for-each>

=====================

<xsl:key name='IDs' match='root/row/child/ID' use='.'/>

<xsl:for-each select='root/row[generate-id() = generate-id(key("IDs",.)[1])]>
    <xsl:call-template name='doSomething'/>
</xsl:for-each>

=====================

<xsl:for-each select='root/row/child/ID[not(. = preceding-sibling::row/child/ID)]>
   <xsl:call-template name='doSomething'/>
</xsl:for-each>

如果这是可怕的编码,我深表歉意,因为这是我第一次尝试使用 XSLT 进行如此复杂的操作,但我仍然无法理解键的使用以及轴语法的工作原理。我通常在 2.0 中使用 for-each-group。但是,在这里尝试 for-each-group 时,我得到所有条目而不是唯一条目,这让我觉得我不完全理解分组的工作原理。我最初的尝试是这样的:

<xsl:for-each-group select='root/row' group-by='child/ID'>
    <xsl:call-template name='doSomething'/>
</xsl:for-each-group>

=====================

我也试过这个:

<xsl:for-each-group select='root/row' group-by='child/ID[1]'>
   <xsl:call-template name='doSomething'/>
</xsl:for-each-group>

=====================

我知道这并不完全正确,但我没有想法,只是在试验。任何解决此问题的指导或帮助将不胜感激。提前感谢您提供的任何帮助。

这是我想要的输出:

001日期1 002日期2

但是,我得到:

001日期1 002日期2 002日期1 002日期3

编辑:感谢您的反馈,我意识到我遗漏了相当重要的代码部分。每个 ID 也有相关的日期,代表交易日期。虽然我可以使用 for-each-group 按 ID 分组,但我继续获取与该 ID 关联的所有日期,并且我只想调用唯一 ID 的模板,而不是包含该 ID 的所有条目 - 这就是正在发生。我添加了一个样本输出和当前输出。

我相信我可能误解了 apply-template 的工作原理。我想如果我使用 apply-templates 它将应用 xslt 中的所有模板。因为我有 4 个在特定条件下使用的其他模板。实际输入的 xml 有大约 40 个子节点,用于处理逻辑和信息。

我希望澄清,如果您需要更多详细信息,请告诉我。

编辑:

在做了更多研究之后,似乎使用密钥对我来说是一个好方法;但是,我似乎无法使语法正确。这是我的尝试,有人可以帮我纠正吗?

<xsl:key name='IDs' match='child' use='ID'/>

<xsl:template match='/'>
   <xsl:for-each select='root/row/child[generate-id(.) = generate-id(key("IDs", ID)[1])]>
       <xsl:call-template name='doSomething'/>
   </xsl:for-each>
</xsl:template>

提前感谢您的任何澄清。

4

3 回答 3

0

通过向我们展示您想要的输出、您拥有的代码、您得到的结果,您还没有真正解释问题是什么。

我看不出有任何问题,apply-templates或者如果您坚持将其call-template与您发布的内容一起使用for-each-group

这是显示这两种方法的示例:

<xsl:stylesheet version="2.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output indent="yes"/>

<xsl:template match="/">
  <apply-templates>
     <xsl:for-each-group select="root/row" group-by="child/ID">
       <xsl:apply-templates select="."/>
     </xsl:for-each-group>
   </apply-templates>
   <call-template>
     <xsl:for-each-group select="root/row" group-by="child/ID">
       <xsl:call-template name="foo"/>
     </xsl:for-each-group>
   </call-template>
</xsl:template>

<xsl:template match="root/row">
  <xsl:copy-of select="."/>
</xsl:template>

<xsl:template name="foo">
  <xsl:copy-of select="."/>
</xsl:template>

</xsl:stylesheet>

当应用于输入时

<root>
  <row>
     <child>
        <ID>value</ID>
     </child>
  </row>
  <row>
     <child>
        <ID>value2</ID>
     </child>
  </row>
  <row>
     <child>
        <ID>value2</ID>
     </child>
  </row>
</root>

使用 Saxon 9.4 我得到了输出

<apply-templates>
   <row>
     <child>
        <ID>value</ID>
     </child>
  </row>
   <row>
     <child>
        <ID>value2</ID>
     </child>
  </row>
</apply-templates>
<call-template>
   <row>
     <child>
        <ID>value</ID>
     </child>
  </row>
   <row>
     <child>
        <ID>value2</ID>
     </child>
  </row>
</call-template>

因此,据我了解您的描述,这两种方法都会处理row您感兴趣的元素。

如果您仍然有问题,请展示最小但完整的 XSLT 示例、您获得的输出、您想要的示例以及您使用的 XSLT 2.0 处理器的详细信息,以便我们重现该问题。

于 2013-01-11T10:35:58.230 回答
0

解决方案

感谢所有提供反馈的人,经过周末的进一步研究和一些实验,我能够得到解决方案!

输入:

<root>
  <row>
     <date>date1</date>
     <child>
         <id>001</id>
     </child>
  </row>
  <row>
     <date>date1</date>
     <child>
         <id>002</id>
     </child>
  </row>
  <row>
     <date>date2</date>
     <child>
         <id>002</id>
     </child>
  </row>
</root>

XSLT:

<xsl:for-each select='root/row'>
    <xsl:if test='preceding-sibling::row[1]/child/id != child/id'>
         <xsl:call-template name='doSomething'/>
    </xsl:if>
</xsl:for-each>

输出:

001日期1, 002日期1

于 2013-01-14T17:00:00.243 回答
0

抱歉,此回复的延迟。这是我想出的解决方案。我不知道这是否是最优雅的解决方案,但它对我来说效果很好。也许一些关于虽然这将是一个糟糕的解决方案的反馈将不胜感激。

    <xsl:for-each-group select='root/Row' group-by='concat(child/ID,date)'>
        <xsl:sort select='child/ID'/>
        <xsl:call-template name='DoSomething'/>
    </xsl:for-each-group>

谢谢你。

于 2013-02-18T05:11:32.683 回答