我有一个非常扁平的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>元素,我就不能输出<c-things>,但是我提前知道完整的类型列表是什么,而且它相当短,所以为每种类型手工编写模板绝对是可能的。我觉得我可以用<xsl:if>和<xsl:for-each>一起破解一些东西,但我也觉得必须有更多的……“‘templatey”的方式。有人能帮上忙吗?
干杯。
发布于 2010-03-26 01:31:36
在XSLT 2中,您可以很好地做到这一点。假设您有一个模板,用于在元素中包装每个对象之前对其进行格式化:
<xsl:template match="thing" mode="format-thing">
<xsl:value-of select="value/text()"/>
</xsl:template>然后,您可以将其应用于某些$type的每个元素,以通过一个函数构建元素:
<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>发布于 2010-03-26 01:01:57
在使用Saxon时,请使用原生XSLT2.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中,可以使用键进行分组。这种方法被称为慕尼黑分组。
键定义了一个包含thing元素的索引,这些元素按照type元素的字符串值进行分组。函数key()返回键中具有指定值的所有节点。
外部的xsl: for -each选择key()最先返回的thing元素的值。
<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>发布于 2010-03-26 01:07:22
一般的解决方案是使用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>https://stackoverflow.com/questions/2516869
复制相似问题