我有以下XML:
<option>
<title>ABC</title>
<desc>123</desc>
</option>
<option>
<title>ABC</title>
<desc>12345</desc>
</option>
<option>
<title>ABC</title>
<desc>123</desc>
</option>
<option>
<title>EFG</title>
<desc>123</desc>
</option>
<option>
<title>EFG</title>
<desc>456</desc>
</option>
使用XSLT,我想将其转换为:
<choice>
<title>ABC</title>
<desc>123</desc>
<desc>12345</desc>
</choice>
<choice>
<title>EFG</title>
<desc>123</desc>
<desc>456</desc>
</choice>
答案 0 :(得分:2)
我建议调查“分组”来解决这个问题。 XSLT 2.0的内置分组功能,如for-each-group,或者,如果您使用的是XSLT 1,则称为“Muenchian Grouping”。
答案 1 :(得分:1)
这是一个最小的XSLT 2.0解决方案:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<choices>
<xsl:for-each-group select="*/title" group-by=".">
<choice>
<title>
<xsl:sequence select="current-grouping-key()"/>
</title>
<xsl:for-each-group select="current-group()/../desc" group-by=".">
<xsl:sequence select="."/>
</xsl:for-each-group>
</choice>
</xsl:for-each-group>
</choices>
</xsl:template>
</xsl:stylesheet>
请注意使用函数current-group()
和current-grouping-key()
答案 2 :(得分:0)
你已经得到了很好的答案。在简洁的过程中,我提出了基于Dimitres answer的16行解决方案:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<choices>
<xsl:for-each-group select="option" group-by="title">
<choice>
<xsl:sequence select="title"/>
<xsl:for-each-group select="current-group()/desc" group-by=".">
<xsl:sequence select="."/>
</xsl:for-each-group>
</choice>
</xsl:for-each-group>
</choices>
</xsl:template>
</xsl:stylesheet>
请注意,for-each-group
中的当前上下文节点是当前组中的第一个项目,而current-group()
返回当前组中所有项目的列表。我利用title
元素对于输入和输出是相同的事实,并从每个组中复制第一个标题。
为了完整起见,使用Muenchian分组的XSLT 1.0解决方案(20行):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="title" match="option/title" use="."/>
<xsl:key name="desc" match="option/desc" use="."/>
<xsl:template match="/*">
<choices>
<xsl:for-each select="option/title[count(.|key('title',.)[1]) = 1]">
<choice>
<xsl:copy-of select="."/>
<xsl:for-each select="key('title',.)/../desc
[count(.|key('desc', .)[../title=current()][1]) = 1]">
<xsl:copy-of select="."/>
</xsl:for-each>
</choice>
</xsl:for-each>
</choices>
</xsl:template>
</xsl:stylesheet>