首先,我知道这个问题:XSLT: Loop selecting two elements at a time
但是由于元素结构,我没有发现它工作,或者我只是使用mod(两者之一)失败了。
<input>
<node>
<id>1</id>
<value>3</value>
</node>
<node>
<id>1</id>
<value>3</value>
</node>
<node>
<id>1</id>
<value>3</value>
</node>
<node>
<id>1</id>
<value>3</value>
</node>
<node>
<id>2</id>
<value>4</value>
</node>
<node>
<id>2</id>
<value>4</value>
</node>
<node>
<id>2</id>
<value>4</value>
</node>
<node>
<id>2</id>
<value>4</value>
</node>
</input>
我有以下XML格式,它具有以下结构: - 相同ID的节点将始终组合在一起 - 一个ID总会有四个节点
我希望能够一次选择一个ID的四个节点并遍历每组四个节点,这样我就可以将数据操作到一个输出行。
最好的方法是什么?
答案 0 :(得分:1)
这个XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:key name="keyByID" match="node" use="id"/>
<xsl:template match="/">
<output>
<xsl:apply-templates/>
</output>
</xsl:template>
<xsl:template match="input">
<xsl:for-each select="node[generate-id()=generate-id(key('keyByID',id)[1])]">
<block>
<id>
<xsl:value-of select="id"/>
</id>
<value>
<xsl:value-of select="value"/>
</value>
</block>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
应用于您的输入XML:
<?xml version="1.0" encoding="UTF-8"?>
<input>
<node>
<id>1</id>
<value>3</value>
</node>
<node>
<id>1</id>
<value>3</value>
</node>
<node>
<id>1</id>
<value>3</value>
</node>
<node>
<id>1</id>
<value>3</value>
</node>
<node>
<id>2</id>
<value>4</value>
</node>
<node>
<id>2</id>
<value>4</value>
</node>
<node>
<id>2</id>
<value>4</value>
</node>
<node>
<id>2</id>
<value>4</value>
</node>
</input>
给出了这个分组的输出XML:
<?xml version="1.0" encoding="UTF-8"?>
<output>
<block>
<id>1</id>
<value>3</value>
</block>
<block>
<id>2</id>
<value>4</value>
</block>
</output>
输出按<id>
分组。那是你在找什么?我不确定。这个Muenchian Grouping只是简化了你的结构。
祝你好运, 彼得
答案 1 :(得分:1)
由于node
元素保证总是以4为一组,所以可以通过一个非常简单的转换生成所需的输出,该转换不使用任何分组方法(例如Muenchian或兄弟姐妹比较)并且可能更有效:
<xsl:stylesheet version="1.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="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node">
<sample>
<xsl:call-template name="identity"/>
</sample>
</xsl:template>
<xsl:template match="node[not(position() mod 4 = 1)]"/>
</xsl:stylesheet>
在提供的XML文档上应用此转换时:
<input>
<node>
<id>1</id>
<value>3</value>
</node>
<node>
<id>1</id>
<value>3</value>
</node>
<node>
<id>1</id>
<value>3</value>
</node>
<node>
<id>1</id>
<value>3</value>
</node>
<node>
<id>2</id>
<value>4</value>
</node>
<node>
<id>2</id>
<value>4</value>
</node>
<node>
<id>2</id>
<value>4</value>
</node>
<node>
<id>2</id>
<value>4</value>
</node>
</input>
产生了想要的正确结果:
<input>
<sample>
<node>
<id>1</id>
<value>3</value>
</node>
</sample>
<sample>
<node>
<id>2</id>
<value>4</value>
</node>
</sample>
</input>
<强>解释强>:
identity rule 按“原样”复制选择执行它的每个节点。
另一个模板会覆盖每个node
元素的标识模板,该元素是其父级的4k+1
st node
子元素。此模板生成包装元素(sample
),然后按名称调用标识模板将自身复制到输出。
另一个模板会覆盖每个node
元素的标识模板。此模板与每个node
元素匹配,但它将被选择执行(优先于之前的模板),仅适用于与之前模板不匹配的节点 - 适用于任何不是{a}}元素的node
元素其父母的4k+1
st node
孩子。之所以这样,是因为此模板不太具体而不是之前的模板。
上面讨论的模板3没有正文,这有效地从输出中“删除”匹配的node
元素。
答案 2 :(得分:0)
如果你可以保证相同id的节点总是像那样相邻,那么
<xsl:template match="node[not(preceding-sibling::node[1]/id = id])">
将匹配每个id的第一个节点(技术上任何id与之前的节点不同的节点,如果有的话),并且在该模板中你可以使用{{1找到其他人,或者只是在顶层定义一个键然后用它来提取具有相同id的所有节点。