I have XML documents which I want to transfer into another structure. An example of a document looks as follows:
<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>
Now what I want to do is group the elements by country, meaning the result should look like this:
<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>
I am using an XPath for-each pattern to select all countries, but it doesn’t do what it is supposed to do. My pattern looks as follows:
<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>
The error is probably somewhere in the XPath expression which does not compile. I changed it to the following
<xsl:for-each select="/application/contactPerson/countryCode[not(.=preceding-sibling::*)]">
because I think I am already at the right position of my tree. This solution compiles, but it doesn’t give me a unique list of countries as I intended by using “preceding-sibling” but the complete list instead.
Besides that I need the solution to my problem, I would be especially thankful for some help on understanding what is actually happening here.
- Is it possible to give a relative path as in my second solution or do I have to give the complete path each time?
- Am I on the right track for making a unique list of country codes, or would that generally be implemented differently?
- Is the solution I am looking for actually implementable with XSLT?
I appreciate your help very much.
Your XPath expression was almost correct:
The reason it didn’t work is that countryCode doesn’t have any siblings. To find the previous countryCodes, you need to move up one level and then back down:
I believe that should successfully iterate through the distinct countryCodes.
This modification to your template should work. You needed another loop to iterate through all the people for each country: