使用XSLT重新排列XML同级



我正在使用XSLT将一个XML文档转换为一个更有意义的新XML文档。源XML:

<root>
<A>
<country>Italy</country>
<city>Rome</city>
<score>13</score>
</A>
<A>
<country>Italy</country>
<city>Florence</city>
<score>14</score>
</A>
<A>
<country>France</country>
<city>Paris</city>
<score>20</score>
</A>
</root>

节点<country><city><score>都是兄弟节点。我的问题是:如何在XSLT中像这样重新排列兄弟?

<country>
<city>
<score>
</score>
</city>
</country>

我期望的XML:

<root>
<Italy>
<Rome>
<score>13</score>
</Rome>
<Florence>
<score>14</score>
</Florence>
</Italy>
<France>
<Paris>
<score>20</score>
</Paris>
</France>
</root>

XSLT-1.0解决方案如下。它使用Muenchian Grouping作为获取唯一国家/地区值的方法。

EDIT:
为了确保元素名称是有效的QNames,我添加了一个translate(...)表达式,它将各个城市名称或国家名称中的所有空格转换为下划线。

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="countries" match="A" use="country" />
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
<xsl:template match="text()" />    
<xsl:template match="A[generate-id(.) = generate-id(key('countries',country)[1])]">
<xsl:element name="{translate(country,' ','_')}">
<xsl:for-each select="key('countries',country)">
<xsl:element name="{translate(city,' ','_')}">
<xsl:copy-of select="score" />
</xsl:element>           
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

XSLT-2.0解决方案更容易,因为它可以使用xsl:for-each-group:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/*">
<xsl:copy>
<xsl:for-each-group select="A" group-by="country">
<xsl:element name="{translate(current-grouping-key(),' ','_')}">
<xsl:for-each select="current-group()">
<xsl:element name="{translate(city,' ','_')}">
<xsl:copy-of select="score" />
</xsl:element>           
</xsl:for-each>
</xsl:element>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

两种方法的输出相同:

<?xml version="1.0"?>
<root>
<Italy>
<Rome>
<score>13</score>
</Rome>
<Florence>
<score>14</score>
</Florence>
</Italy>
<France>
<Paris>
<score>20</score>
</Paris>
</France>
</root>

最新更新