我有XML文档,我想将其转移到另一个结构中。文档示例如下:
<application>
<contactPerson>
<name>Dominik</name>
<countryCode>DE</countryCode>
</contactPerson>
<contactPerson>
<name>Dorothea</name>
<countryCode>DE</countryCode>
</contactPerson>
<contactPerson>
<name>Fiona</name>
<countryCode>FR</countryCode>
</contactPerson>
<contactPerson>
<name>Fabian</name>
<countryCode>FR</countryCode>
</contactPerson>
<contactPerson>
<name>Florian</name>
<countryCode>FR</countryCode>
</contactPerson>
<contactPerson>
<name>Gabi</name>
<countryCode>GB</countryCode>
</contactPerson>
<contactPerson>
<name>Gert</name>
<countryCode>GB</countryCode>
</contactPerson>
</application>
现在我要做的是按国家/地区对元素进行分组,这意味着结果应如下所示:
<application>
<memberState>
<countryCode>De</countryCode>
<contactPerson>
<name>Dominik</name>
</contactPerson>
<contactPerson>
<name>Dorothea</name>
</contactPerson>
</memberState>
<memberState>
<countryCode>FR</countryCode>
<contactPerson>
<name>Fiona</name>
</contactPerson>
<contactPerson>
<name>Fabian</name>
</contactPerson>
<contactPerson>
<name>Florian<name>
</contactPerson>
</memberState>
<memberState>
<countryCode>GB</countryCode>
<contactPerson>
<name>Gabi</name>
</contactPerson>
<contactPerson>
<name>Gert</name>
</contactPerson>
</memberState>
</application>
我正在使用XPath for-each模式来选择所有国家/地区,但它没有按预期执行。我的模式如下:
<xsl:template match="/">
<application>
<xsl:for-each select="/application/contactPerson/countryCode[not(.=preceding-sibling::*/application/contactPerson/countryCode)]">
<memberState>
<countryCode>
<xsl:value-of select="."/>
</countryCode>
<contactPerson>
<name>
<xsl:value-of select="../name"/>
</name>
</contactPerson>
</memberState>
</xsl:for-each>
</application>
</xsl:template>
错误可能在XPath表达式中的哪个地方无法编译。我将其更改为以下
<xsl:for-each select="/application/contactPerson/countryCode[not(.=preceding-sibling::*)]">
因为我认为我已经在树的正确位置了。这个解决方案可以编译,但它不会像我预期的那样使用“previous-sibling”来提供一个唯一的国家列表,而是使用完整的列表。
除了我需要解决我的问题之外,我还要特别感谢一些帮助,以了解这里实际发生的事情。
非常感谢你的帮助。
答案 0 :(得分:2)
您可以使用key
标记通过countryCode访问每个元素,如下所示:
<xsl:key name="groups" match="/application/contactPerson" use="countryCode" />
然后您可以迭代该键的内容。见Walk/loop through an XSL key: how?
因此,如果您坚持使用XSLT 1.0,那将是一个很好的开始。如果您可以使用XSLT 2.0,那么您应该查看group-by
。
答案 1 :(得分:2)
以下是使用Muenchian grouping的XSLT 1.0解决方案:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:key name="country" match="contactPerson" use="countryCode"/>
<xsl:template match="application">
<xsl:copy>
<xsl:apply-templates select="contactPerson[generate-id() = generate-id(key('country', countryCode)[1])]" mode="group"/>
</xsl:copy>
</xsl:template>
<xsl:template match="contactPerson" mode="group">
<memberState>
<xsl:copy-of select="countryCode"/>
<xsl:apply-templates select="key('country', countryCode)"/>
</memberState>
</xsl:template>
<xsl:template match="contactPerson">
<xsl:copy>
<xsl:copy-of select="*[not(self::countryCode)]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
答案 2 :(得分:1)
你的XPath表达式几乎是正确的:
<xsl:for-each
select="/application/contactPerson/countryCode[not(.=preceding-sibling::*)]">
它不起作用的原因是countryCode没有任何兄弟姐妹。要查找以前的countryCodes,您需要向上移动一个级别然后向下移动:
<xsl:for-each
select="/application/contactPerson/countryCode[not(. = ../preceding-sibling::*/countryCode)]">
我认为应该成功地遍历不同的countryCodes。
对模板的这种修改应该有效。你需要另一个循环遍历每个国家的所有人:
<xsl:template match="/">
<application>
<xsl:for-each select="/application/contactPerson/countryCode[not(.=../preceding-sibling::*/countryCode)]">
<memberState>
<countryCode>
<xsl:value-of select="."/>
</countryCode>
<xsl:for-each select="/application/contactPerson[countryCode = current()]">
<contactPerson>
<name>
<xsl:value-of select="name"/>
</name>
</contactPerson>
</xsl:for-each>
</memberState>
</xsl:for-each>
</application>
</xsl:template>