我有以下形式的XML:
<Set>
<Element name="Superset1_Set1_Element1"/>
<Element name="Superset1_Set1_Element2"/>
<Element name="Superset1_Set2_Element1"/>
<Element name="Superset2_Set1_Element1"/>
<Element name="Superset2_Set2_Element1"/>
</Set>
我希望将其转变为形式的XML:
<Superset name="Superset1">
<Set name="Set1">
<Element name="Element1"/>
<Element name="Element2"/>
</Set>
<Set name="Set2">
<Element name="Element1"/>
</Set>
</Superset>
<Superset name="Superset2">
<Set name="Set1">
<Element name="Element1"/>
</Set>
<Set name="Set2">
<Element name="Element1"/>
</Set>
</Superset>
怎么可以这样用XSLT做了什么?
非常感谢提前!
这可以用下面的XSLT 1.0变换来解决:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<!-- this key selects elements by their "Superset" name -->
<xsl:key name="kElementBySuperset" match="Element" use="
substring-before(@name, '_')"
/>
<!-- this key selects elements by their "Superset_Set" name -->
<xsl:key name="kElementBySet" match="Element" use="
concat(
substring-before(@name, '_'),
'_',
substring-before(substring-after(@name, '_'), '_')
)
" />
<!--- initalize output (note the template modes) -->
<xsl:template match="Set">
<xsl:apply-templates select="Element" mode="Superset">
<xsl:sort select="@name" />
</xsl:apply-templates>
</xsl:template>
<!-- output <Superset> elements, grouped by name -->
<xsl:template match="Element" mode="Superset">
<xsl:variable name="vSupersetName" select="
substring-before(@name, '_')
" />
<xsl:if test="
generate-id()
=
generate-id(key('kElementBySuperset', $vSupersetName)[1])
">
<Superset name="{$vSupersetName}">
<xsl:apply-templates
select="key('kElementBySuperset', $vSupersetName)"
mode="Set"
>
<xsl:sort select="@name" />
</xsl:apply-templates>
</Superset>
</xsl:if>
</xsl:template>
<!-- output <Set> elements, grouped by name -->
<xsl:template match="Element" mode="Set">
<xsl:variable name="vSetName" select="
concat(
substring-before(@name, '_'),
'_',
substring-before(substring-after(@name, '_'), '_')
)"
/>
<xsl:if test="
generate-id()
=
generate-id(key('kElementBySet', $vSetName)[1])
">
<Set name="{substring-after($vSetName, '_')}">
<xsl:apply-templates
select="key('kElementBySet', $vSetName)"
mode="Element"
>
<xsl:sort select="@name" />
</xsl:apply-templates>
</Set>
</xsl:if>
</xsl:template>
<!-- output <Element> elements -->
<xsl:template match="Element" mode="Element">
<xsl:variable name="vElementName" select="
substring-after(
substring-after(@name, '_'),
'_'
)
" />
<Element name="{$vElementName}" />
</xsl:template>
</xsl:stylesheet>
我的系统上的输出,当适用于您的输入文档:
<Superset name="Superset1">
<Set name="Set1">
<Element name="Element1" />
<Element name="Element2" />
</Set>
<Set name="Set2">
<Element name="Element1" />
</Set>
</Superset>
<Superset name="Superset2">
<Set name="Set1">
<Element name="Element1" />
</Set>
<Set name="Set2">
<Element name="Element1" />
</Set>
</Superset>
值得注意的是,这种解决方案是区分大小写的。 我假设你的情况是理想的(或至少不会对人体有害)。 如果需要不区分大小写,然后洒的这些少数会成为必要(其中“...”,当然必须由缺字母代替):
translate($anyvalue, 'ABC…XYZ', 'abc…xyz')
我避免了因为它是非常重复,使得该解决方案(甚至更多)晦涩。
延伸阅读:我的一个解决方案,做了类似的两步使用两个分组<xsl:key>
s是在这里:
在属性XSLT 3级分组
它是在内部更详细一点,它包含一个冗长的解释<xsl:key>
我想避免重复在这里。 ;-)
保存为参考,但仅凭我强烈建议使用模板(即托默勒格的解决方案),其中可能的可读性...
当然有可能,但比我预期的,因为二阶集和双下划线的实际困难 - 如果“名称”值是一个友好的格式如下当然可以得到改善。
<xsl:key name="supers" match="Set/Element" use="substring-before(@name,'_')"/>
<xsl:key name="sets" match="Set/Element" use="concat(substring-before(@name,'_'),'_',substring-before(substring-after(@name,'_'),'_'))"/>
<xsl:template match="/">
<xsl:for-each select="Set/Element[generate-id() = generate-id(key('supers',substring-before(@name,'_'))[1])]">
<xsl:variable name="super" select="substring-before(@name,'_')"/>
<Superset name="{$super}">
<xsl:for-each select="//Set/Element[generate-id() = generate-id(key('sets',concat($super,'_',substring-before(substring-after(@name,'_'),'_')))[1])]">
<Set name="{substring-before(substring-after(@name,'_'),'_')}">
<xsl:variable name="set" select="concat($super,'_',substring-before(substring-after(@name,'_'),'_'))"/>
<xsl:for-each select="//Set/Element[starts-with(@name,$set)]">
<Element name="{substring-after(substring-after(@name,'_'),'_')}"/>
</xsl:for-each>
</Set>
</xsl:for-each>
</Superset>
</xsl:for-each>
</xsl:template>
诀窍就是Muenchian分组的 ,并获得正确的键值。
这真的不是很漂亮,所以我敢肯定有提供更好的解决方案,但我jetlagged:P