我有一个非常扁平的XML结构,我需要重新排序到分类的部分,并且在我的生活中,我无法弄清楚如何在XSLT中做到这一点(不是说我无论如何都是专家。 )
基本上,原始XML看起来有点像:
<things>
<thing>
<value>one</value>
<type>a</type>
</thing>
<thing>
<value>two</value>
<type>b</type>
</thing>
<thing>
<value>thee</value>
<type>b</type>
</thing>
<thing>
<value>four</value>
<type>a</type>
</thing>
<thing>
<value>five</value>
<type>d</type>
</thing>
</things>
我需要输出类似的内容:
<data>
<a-things>
<a>one</a>
<a>four</a>
</a-things>
<b-things>
<b>two</b>
<b>three</b>
</b-things>
<d-things>
<d>five</d>
</d-things>
</data>
请注意,如果没有任何<c-things>
元素,我就无法输出<c>
,但我事先知道完整的类型列表是什么,并且它很短,所以手写模板对于每种类型绝对是可能的。感觉好像我可以使用<xsl:if>
和<xsl:for-each>
一起破解某些东西,但也感觉必须有更多......'模板'的方法来做到这一点。有人可以帮忙吗?
干杯。
答案 0 :(得分:4)
在使用Saxon时,请使用本机XSLT 2.0分组。
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="things">
<data>
<xsl:for-each-group select="thing" group-by="type">
<xsl:element name="{concat(current-grouping-key(),'-things')}">
<xsl:for-each select="current-group()">
<xsl:element name="{current-grouping-key()}">
<xsl:value-of select="value" />
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:for-each-group>
</data>
</xsl:template>
</xsl:stylesheet>
在XSLT 1.0中,您可以使用键进行分组。这种方法称为 Muenchian Grouping 。
xsl:key定义包含thing
元素的索引,按其type
元素的字符串值进行分组。函数key()
返回具有指定值的键中的所有节点。
外部xsl:for-each选择由thing
返回的key()
元素作为其值。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:key name="thing" match="thing" use="type" />
<xsl:template match="things">
<data>
<xsl:for-each select="thing[generate-id(.)=generate-id(key('thing',type)[1])]">
<xsl:element name="{concat(type,'-things')}">
<xsl:for-each select="key('thing',type)">
<xsl:element name="{type}">
<xsl:value-of select="value" />
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:for-each>
</data>
</xsl:template>
</xsl:stylesheet>
答案 1 :(得分:1)
通用解决方案是使用XSL密钥:
<xsl:key name="kThingByType" match="thing" use="type" />
<xsl:template match="things">
<xsl:copy>
<xsl:apply-templates select="thing" mode="group">
<xsl:sort select="type" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="thing" mode="group">
<xsl:variable name="wholeGroup" select="key('kThingByType', type)" />
<xsl:if test="generate-id() = generate-id($wholeGroup[1])">
<xsl:element name="{type}-thing">
<xsl:copy-of select="$wholeGroup/value" />
</xsl:element>
</xsl:if>
</xsl:template>
以上产量:
<things>
<a-thing>
<value>one</value>
<value>four</value>
</a-thing>
<b-thing>
<value>two</value>
<value>thee</value>
</b-thing>
<d-thing>
<value>five</value>
</d-thing>
</things>
答案 2 :(得分:0)
当然,问这个问题很明显......
我的解决方案确实使用<xsl:if>
,但我现在看不出它是怎么回事。我的解决方案基本上看起来像:
<xsl:if test="/things/thing/type = 'a'">
<a-things>
<xsl:apply-templates select="/things/thing[type='a']" mode="a" />
</a-things>
</if>
<xsl:template match="/things/thing[type='a']" mode="a">
<a><xsl:value-of select="value"/>
</xsl:template>
并重复其他类型。我把它编码了,似乎工作得很好。
答案 3 :(得分:0)
<a-things>
<xsl:for-each select="thing[type = 'a']">
<a><xsl:value-of select="./value" /></a>
</xsl:for-each>
</a-things>
如果你想变得时髦,请用参数替换<a-things>
和谓词,并使用属性值模板:
<xsl:param name="type" />
<xsl:element name="{$type}-things">
<xsl:for-each select="thing[type = $type]">
<xsl:element name="{$type}"><xsl:value-of select="./value" /></xsl:element>
</xsl:for-each>
</xsl:element>
答案 4 :(得分:0)
使用分组,您可以在没有if:
的情况下进行分组<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="things">
<data>
<xsl:for-each select="thing[not(type=preceding-sibling::thing/type)]">
<xsl:variable name="type"><xsl:value-of select="type" /></xsl:variable>
<xsl:element name="concat($type, '-things')">
<xsl:for-each select="../thing[type=$type]">
<xsl:element name="$type">
<xsl:value-of select="value" />
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:for-each>
</data>
</xsl:template>
</xsl:stylesheet>
答案 5 :(得分:0)
在XSLT 2中,您可以非常优雅地执行此操作。假设您有一个模板用于格式化每个事物,然后将其包装在&lt; a&gt;中。元素:
<xsl:template match="thing" mode="format-thing">
<xsl:value-of select="value/text()"/>
</xsl:template>
然后,您可以将其应用于某些$type
的每个内容,以构建&lt; a-things&gt;元素通过函数:
<xsl:function name="my:things-group" as="element()">
<xsl:param name="type" as="xs:string"/>
<xsl:param name="things" as="element(thing)*"/>
<xsl:element name="{ concat($type, '-things') }">
<xsl:for-each select="$things[type/text() eq $type]">
<xsl:element name="{ $type }">
<xsl:apply-templates select="." mode="format-thing"/>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:function>
然后你可以为每个唯一类型(样本输入中的a,b,d)调用该函数来构建整个输出,然后你就完成了:
<xsl:template match="/">
<data>
<xsl:sequence select="
for $type in distinct-values(things/thing/type/text())
return my:things-group($type, /things/thing)
"/>
</data>
</xsl:template>