我目前正在使用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-groups时,我得到所有条目而不是唯一条目,这让我觉得我不完全理解分组是如何工作的。我原来的尝试是这样的:
<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>
====================
我知道这不完全正确,但我的想法用完了,只是在尝试。任何解决这个问题的方向或协助都会非常感激。提前感谢您提供的任何帮助。
这是我想要的输出:
001date1 002date2
但是,我得到了:
001date1 002date2 002date1 002date3
编辑:感谢您的反馈,我意识到我遗漏了相当重要的代码部分。还有与每个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>
提前感谢您的任何澄清。
答案 0 :(得分: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处理器的详细信息,以便我们可以重现该问题。
答案 1 :(得分: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>
输出:
001date1,002date1
答案 2 :(得分: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>
谢谢。