Im now to XSLT and I need to do some grouping with the value of a child. I have objects, that can have multiple properties and one of them (with id=11) is on I need to use as a grouping element. The amount of properties each object can have varies, but they all have the common prop.
Input:
<object id=1>
<properties>
<prop id=10>Name of object 1</prop>
<prop id=11>Group 1</prop>
<prop id=xy>Whatever properties this object has</prop>
</properties>
</object>
<object id=2>
<properties>
<prop id=10>Name of object 2</prop>
<prop id=11>Group 2</prop>
</properties>
</object>
<object id=3>
<properties>
<prop id=10>Name of object 3</prop>
<prop id=11>Group 1</prop>
</properties>
</object>
<object id=4>
<properties>
<prop id=10>Name of object 4</prop>
<prop id=11>Group 3</prop>
</properties>
</object>
Desired output:
<group name='Group 1'>
<object id=1>
<prop id=10>Name of object 1</prop>
<prop id=11>Group 1</prop>
<prop id=xy>Whatever properties this object has</prop>
</object>
<object id=3>
<properties>
<prop id=10>Name of object 3</prop>
<prop id=11>Group 1</prop>
</properties>
</object>
</group>
<group name='Group 2'>
<object id=2>
<properties>
<prop id=10>Name of object 2</prop>
<prop id=11>Group 2</prop>
</properties>
</object>
</group>
<group name='Group 3'>
<object id=4>
<properties>
<prop id=10>Name of object 4</prop>
<prop id=11>Group 3</prop>
</properties>
</object>
</group>
The idea is to have the items grouped by the value of the prop with id 11.
Ive found multiple different code samples, but none of them have had this specific case, and I havent been able to modify them to suit my need.
My version of the xsl that is essentially that same as Michael Vehrs in that it is also using the Muenchian Method but thought I may as well post it to show a subtle alternative. Here I'm using an apply-templates instead of the for-each/call-template. In terms of performance there is probably nothing between them but personally, I prefer to use apply-templates wherever possible as it typically allows for more flexibility.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="no" encoding="utf-8"/>
<xsl:key name="groups" match="prop[#id='11']" use="text()"/>
<xsl:template match="/">
<xsl:apply-templates select="//prop[generate-id()=generate-id(key('groups', text())[1])]"/>
</xsl:template>
<xsl:template match="prop">
<group name="{text()}">
<xsl:copy-of select="//object[properties/prop[#id='11']=current()/text()]"/>
</group>
</xsl:template>
</xsl:stylesheet>
This is a basic application of the Muenchian Method (which is explained here, for example):
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="prop" match="//prop[#id='11']/text()" use="." />
<xsl:template match="/">
<xsl:for-each select="//prop[#id='11']/text()[generate-id()
= generate-id(key('prop',.)[1])]">
<xsl:call-template name="group">
<xsl:with-param name="groupname" select="." />
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="group">
<xsl:param name="groupname" />
<group><xsl:attribute name="name"><xsl:value-of select='$groupname'/></xsl:attribute>
<xsl:for-each select="//object[.//prop[#id='11'][text()=$groupname]]" >
<xsl:copy-of select="." />
</xsl:for-each>
</group>
</xsl:template>
</xsl:stylesheet>
Related
I have the following 2 xml files,
Mapping.xml
<xml>
<map ordinal="0" reverse="xxx" forward="ThisIsXxx" />
<map ordinal="0" reverse="yyy" forward="ThisIsYyy" />
<map ordinal="0" reverse="zzz" forward="thisIsZzz" />
<map ordinal="0" reverse="xx1" forward="ThisIsXx1Info" />
<map ordinal="0" reverse="yy1" forward="ThisIsYy1Info" />
<map ordinal="0" reverse="zz1" forward="ThisIsZz1Info" />
</xml>
and an input xml file with a series of elements with some of them having a 'name' attribute
second.xml
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<Children>
<child name="xxx">
<info name="xx1">
</info>
</child>
<child name="yyy">
<info name="yy1">
</info>
</child>
<child name="zzz">
<info name="zz1">
</info>
</child>
</Children> <!-- Added by edit -->
</xml>
What I need is if direction is 'forward' then the second.xml files #name to be set to the value from #forward from the Mapping.xml such as the following,
output.xml
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<Children>
<child name="ThisIsXxx">
<info name="ThisIsXx1Info">
</info>
</child>
<child name="ThisIsZzz">
<info name="ThisIsYy1Info">
</info>
</child>
<child name="ThisIsYyy">
<info name="ThisIsZz1Info">
</info>
</child>
</xml>
Xslt file I have is setting all #name="" but not getting any values from the external file..
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<!-- get the mapping db -->
<xsl:variable name="mapDb" select="document('Mapping.xml')" />
<xsl:variable name="mapFwd" select="forward" />
<!-- for all #name find the #mapdb/*/#reverse == #name and set #name=#mapDb/*/forward -->
<xsl:template match="*/#name">
<xsl:choose>
<xsl:when test="$mapFwd='forward'">
<xsl:attribute name="name">
<xsl:value-of select="$mapDb/xml/map[#reverse='{#name}']/#forward"/>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="name">
<xsl:value-of select="$mapDb/xml/map[#forward='{#name}']/#reverse"/>
</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--Copy Everything unchanged-->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I would appreciate some help.
I think you want to use current() e.g. $mapDb/xml/map[#reverse=current()] in your comparisons in the template matching the #name attribute and of course the variable should be initialized with <xsl:variable name="mapFwd" select="'forward'"/>.
I am new to xslt and I was trying to merge 2 xml files - expecting something like Left join but its not working as expected. It would be great and much appreciated if you guys could help me on this.
The matching ID's in the second file should merge their data to the first file and ignore the rest of the non-matching Id's.
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
xmlns:map="http://www.w3.org/2005/xpath-functions/map" exclude-result-prefixes="array fn map math xs">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="doc1">
<RECORDS>
<RECORD>
<PROP NAME="A">
<PVAL>text A</PVAL>
</PROP>
<PROP NAME="B">
<PVAL>text B</PVAL>
</PROP>
<PROP NAME="C">
<PVAL>10</PVAL>
</PROP>
<PROP NAME="Id">
<PVAL>1</PVAL>
</PROP>
</RECORD>
<RECORD>
<PROP NAME="D">
<PVAL>text D</PVAL>
</PROP>
<PROP NAME="E">
<PVAL>text E</PVAL>
</PROP>
<PROP NAME="F">
<PVAL>10</PVAL>
</PROP>
<PROP NAME="Id">
<PVAL>2</PVAL>
</PROP>
</RECORD>
<RECORD>
<PROP NAME="G">
<PVAL>text G</PVAL>
</PROP>
<PROP NAME="H">
<PVAL>text H</PVAL>
</PROP>
<PROP NAME="I">
<PVAL>10</PVAL>
</PROP>
<PROP NAME="Id">
<PVAL>3</PVAL>
</PROP>
</RECORD>
</RECORDS>
</xsl:param>
<xsl:param name="doc2">
<RECORDS>
<RECORD>
<PROP NAME="J">
<PVAL>text J</PVAL>
</PROP>
<PROP NAME="K">
<PVAL>text K</PVAL>
</PROP>
<PROP NAME="L">
<PVAL>10</PVAL>
</PROP>
<PROP NAME="Id">
<PVAL>1</PVAL>
</PROP>
</RECORD>
<RECORD>
<PROP NAME="M">
<PVAL>text M</PVAL>
</PROP>
<PROP NAME="N">
<PVAL>text N</PVAL>
</PROP>
<PROP NAME="O">
<PVAL>10</PVAL>
</PROP>
<PROP NAME="Id">
<PVAL>4</PVAL>
</PROP>
</RECORD>
</RECORDS>
</xsl:param>
<xsl:template match="/" name="xsl:initial-template">
<xsl:merge>
<xsl:merge-source select="$doc1/RECORDS/RECORD">
<xsl:merge-key select="PROP/#Id"></xsl:merge-key>
</xsl:merge-source>
<xsl:merge-source select="$doc2/RECORDS/RECORD">
<xsl:merge-key select="PROP/#Id"></xsl:merge-key>
</xsl:merge-source>
<xsl:merge-action>
<xsl:copy>
<xsl:apply-templates select="current-merge-group()[1]/*, fn:current-merge-group()[2]/*"/>
</xsl:copy>
</xsl:merge-action>
</xsl:merge>
</xsl:template>
</xsl:stylesheet>
Expected Result:
<?xml version="1.0" encoding="UTF-8"?>
<RECORDS>
<RECORD>
<PROP NAME="Id">
<PVAL>1</PVAL>
</PROP>
<PROP NAME="A">
<PVAL>text A</PVAL>
</PROP>
<PROP NAME="B">
<PVAL>text B</PVAL>
</PROP>
<PROP NAME="C">
<PVAL>10</PVAL>
</PROP>
<PROP NAME="J">
<PVAL>text J</PVAL>
</PROP>
<PROP NAME="K">
<PVAL>text K</PVAL>
</PROP>
<PROP NAME="L">
<PVAL>10</PVAL>
</PROP>
</RECORD>
<RECORD>
<PROP NAME="Id">
<PVAL>2</PVAL>
</PROP>
<PROP NAME="D">
<PVAL>text D</PVAL>
</PROP>
<PROP NAME="E">
<PVAL>text E</PVAL>
</PROP>
<PROP NAME="F">
<PVAL>10</PVAL>
</PROP>
</RECORD>
<RECORD>
<PROP NAME="Id">
<PVAL>3</PVAL>
</PROP>
<PROP NAME="G">
<PVAL>text G</PVAL>
</PROP>
<PROP NAME="H">
<PVAL>text H</PVAL>
</PROP>
<PROP NAME="I">
<PVAL>10</PVAL>
</PROP>
</RECORD>
</RECORDS>
Your paths don't match the input structure and you haven't checked whether there is a match in the first merge source so the following change should work:
<xsl:template match="/" name="xsl:initial-template">
<xsl:merge>
<xsl:merge-source name="doc1" select="$doc1/RECORDS/RECORD">
<xsl:merge-key select="PROP[#NAME = 'Id']/PVAL"></xsl:merge-key>
</xsl:merge-source>
<xsl:merge-source name="doc2" select="$doc2/RECORDS/RECORD">
<xsl:merge-key select="PROP[#NAME = 'Id']/PVAL"></xsl:merge-key>
</xsl:merge-source>
<xsl:merge-action>
<xsl:if test="current-merge-group('doc1')">
<xsl:copy>
<xsl:apply-templates select="*, current-merge-group('doc2')/*"/>
</xsl:copy>
</xsl:if>
</xsl:merge-action>
</xsl:merge>
</xsl:template>
https://xsltfiddle.liberty-development.net/6q1SDkj
I have an xml like this (and am restricted to using XSLT 1.0 to process it)
<Template>
<ID>someTemplate</ID>
<Object>
<ID>someID</ID>
<Name>someName</Name>
<Association type="someAssociation">
<Object>
<ID>/someUniqueID</ID>
<Context>
<ID>someContextID</ID>
</Context>
<ClassID>someClassID</ClassID>
</Object>
</Association>
<Association type="someAssociation">
<Object>
<ID>/someUniqueID</ID>
<Context>
<ID>someContextID</ID>
</Context>
<ClassID>someClassID</ClassID>
</Object>
</Association>
<Association type="someAssociation">
<Object>
<ID>/someUniqueID</ID>
<Context>
<ID>someContextID</ID>
</Context>
<ClassID>someClassID</ClassID>
</Object>
</Association>
</Object>
</Template>
And I want to remove the character '/' at the start of each value in the ID nodes under the Association/Object nodes. The expected output should look like this;
<Template>
<ID>someTemplate</ID>
<Object>
<ID>someID</ID>
<Name>someName</Name>
<Association type="someAssociation">
<Object>
<ID>someUniqueID</ID>
<Context>
<ID>someContextID</ID>
</Context>
<ClassID>someClassID</ClassID>
</Object>
</Association>
<Association type="someAssociation">
<Object>
<ID>someUniqueID</ID>
<Context>
<ID>someContextID</ID>
</Context>
<ClassID>someClassID</ClassID>
</Object>
</Association>
<Association type="someAssociation">
<Object>
<ID>someUniqueID</ID>
<Context>
<ID>someContextID</ID>
</Context>
<ClassID>someClassID</ClassID>
</Object>
</Association>
</Object>
</Template>
I have been looking at the translate() function, but am struggling to find any way of implementing it.
Write a template matching those ID elements starting with a slash and then simply output the value after the slash with substring:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ID[starts-with(., '/')]">
<xsl:copy>
<xsl:value-of select="substring(., 2)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
You might want or need to adjust the match pattern to Association/Object/ID if you only want to treat these particular ID elements.
I have a situation where I need to select the nodes based on certain conditions and then apply Muenchian grouping to get unique values. I am on XSLT 1.0
<Parent>
<Child>
<A></A>
<B></B>
<C></C>
</Child>
<Child>
<A></A>
<B></B>
<C></C>
</Child>
<Child>
<A></A>
<B></B>
<C></C>
</Child>
</Parent>
The Condition that I have for use attribute is:
If A is not blank, use A. Otherwise use B.
I had thought of a solution that would work for Fixed Width values for Node A and B. So If A and B are of length n, I can do something like this:
<xsl:key name="groupKey" match="/Parent/Child" use="substring(concat(A,B),1,n)">
So, If A is not present, I can use B. But I haven't been able to figure out how to use string-length() to derive the expression for variable lengths for A and B. Any Ideas?
In case you want to use the concat() function:
use="concat(A, B[not(string(../A)])"
And if by blank you mean empty or whitespace-only, then use:
use="concat(A, B[not(normalize-space(../A)])"
Finally, if you really want to use both concat() and substring() (and this is really tricky, so I don't recommend it):
concat(substring(A, 1 div string(A) or 0),
substring(B, 1 div not(string(A)))
)
Here is a complete example for the last case, showing what the call to concat() returns:
XML document:
<t>
<p>
<A>xxx</A>
<B>yyy</B>
</p>
<p>
<A/>
<B>zzz</B>
</p>
</t>
Transformation (just outputs the results of the evaluated expressions):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="p">
<xsl:value-of select=
"concat(substring(A, 1 div string(A) or 0),
substring(B, 1 div not(string(A)))
)"/>
</xsl:template>
</xsl:stylesheet>
Wanted (correct) result produced:
xxx
zzz
How about:
use="A | B[not(string(../A))]"
Here's another option that uses a different use.
Example...
XML Input
<Parent>
<Child>
<A>both</A>
<B>both</B>
<C>c</C>
</Child>
<Child>
<A>a</A>
<B></B>
<C>c</C>
</Child>
<Child>
<A></A>
<B>b</B>
<C>c</C>
</Child>
</Parent>
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="child" match="Child" use="(A[string()]|B[string()])[1]"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<xsl:for-each select="Child[count(.|key('child',(A[string()]|B[string()])[1])[1])=1]">
<group key="{(A[string()]|B[string()])[1]}">
<xsl:apply-templates select="key('child',(A[string()]|B[string()])[1])"/>
</group>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
XML Output
<Parent>
<group key="both">
<Child>
<A>both</A>
<B>both</B>
<C>c</C>
</Child>
</group>
<group key="a">
<Child>
<A>a</A>
<B/>
<C>c</C>
</Child>
</group>
<group key="b">
<Child>
<A/>
<B>b</B>
<C>c</C>
</Child>
</group>
</Parent>
ХМL:
<?xml version="1.0" encoding="utf-8" ?>
<page>
<elements>
<element>
<data>
<Styles Name="default">
<Style Url="/css.css" Browser="default" Version="default"/>
</Styles>
</data>
</element>
<element type="Digillect.WB.Web.Elements.Site.SiteStructureElement">
<config StartLevel="0" MaxDepth="3" UseItemVisibility="false">
<monikers>
<moniker store="asdasd"/>
</monikers>
</config>
<data ParentPath="/">
</data>
</element>
<element name="bids">
<config>
<Object Id="1b61995a-6e22-4b09-af5f-9a50cdaa7863"/>
<Object Id="baa1d3df-0510-4f68-8a41-1b9b22587134"/>
</config>
<data>
<Object Id="id2" Name="Paris">
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id1" Name="Lion">
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id3" Name="Berlin">
<Property Name="COUNTRY">Germany</Property>
<Property Name="WWW" >http://germany.gr</Property>
</Object>
</data>
</element>
</elements>
</page>
It is necessary to bring the country's selectors sorted by name and get rid of duplicates:
<select>
<option value="http://germany.gr">Germany</option>
<option value="france">France</option>
</select>
That is, if the country meets a few times, in value = specify the id. If once, then specify the link
Description grouped as follows: if a country meets several times
Code:
<div id="france">
<p> Lion</p>
<p>Paris</p>
</div>
If the country meets once - did not write
This transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kObjByCountry" match="Object" use="Property[#Name='COUNTRY']"/>
<xsl:template match="/">
<xsl:variable name="vCountries" select=
"/*/*/*/data/Object
[generate-id()
= generate-id(key('kObjByCountry', Property[#Name='COUNTRY'])[1])
]"/>
<select>
<xsl:apply-templates select="$vCountries">
<xsl:sort select="Property[#Name='COUNTRY']"/>
</xsl:apply-templates>
</select>
<xsl:apply-templates select="$vCountries" mode="desc">
<xsl:sort select="Property[#Name='COUNTRY']"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Object">
<xsl:variable name="v2nd"
select="key('kObjByCountry', Property[#Name='COUNTRY'])[2]"/>
<option value="{(#Id[$v2nd]|Property[#Name='WWW'])[1]}">
<xsl:value-of select="Property[#Name='COUNTRY']"/>
</option>
</xsl:template>
<xsl:template mode="desc"
match="Object[key('kObjByCountry', Property[#Name='COUNTRY'])[2]]">
<div id="sity_{#Id}">
<xsl:apply-templates select=
"key('kObjByCountry', Property[#Name='COUNTRY'])
/Property[#Name='NAME']">
<xsl:sort/>
</xsl:apply-templates>
</div>
</xsl:template>
<xsl:template match="Property[#Name='NAME']">
<p><xsl:value-of select="."/></p>
</xsl:template>
<xsl:template mode="desc"
match="Object[not(key('kObjByCountry', Property[#Name='COUNTRY'])[2])]"/>
</xsl:stylesheet>
when applied on the provided XML document:
<page>
<elements>
<element name="bids">
<data>
<Object Id="id1">
<Property Name="NAME" Order="0">Paris</Property>
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id1">
<Property Name="NAME" Order="0">Lion</Property>
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id2">
<Property Name="NAME" Order="0">Berlin</Property>
<Property Name="COUNTRY">Germany</Property>
<Property Name="WWW" >http://germany.gr</Property>
</Object>
</data>
</element>
</elements>
</page>
produces the wanted, correct result -- in which the countries/cities are sorted:
<select>
<option value="id1">France</option>
<option value="http://germany.gr">Germany</option>
</select>
<div id="sity_id1">
<p>Lion</p>
<p>Paris</p>
</div>
Explanation:
Proper use of the Muenchian grouping method and AVT s.
Proper use of xsl:sort.
With XSLT 1.0 you can use Muechian grouping to perform grouping respectively to identify and eliminate duplicates; the stylesheet
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:key name="k1" match="data/Object" use="#Id"/>
<xsl:template match="data">
<select>
<xsl:apply-templates select="Object[generate-id() = generate-id(key('k1', #Id)[1])]"/>
</select>
<xsl:apply-templates select="Object[generate-id() = generate-id(key('k1', #Id)[1]) and key('k1', #Id)[2]]" mode="desc"/>
</xsl:template>
<xsl:template match="data/Object[key('k1', #Id)[2]]">
<option value="{#Id}">
<xsl:value-of select="Property[#Name = 'COUNTRY']"/>
</option>
</xsl:template>
<xsl:template match="data/Object[not(key('k1', #Id)[2])]">
<option value="{Property[#Name = 'WWW']}">
<xsl:value-of select="Property[#Name = 'COUNTRY']"/>
</option>
</xsl:template>
<xsl:template match="data/Object" mode="desc">
<div id="city_{#Id}">
<xsl:apply-templates select="key('k1', #Id)/Property[#Name = 'NAME']" mode="desc"/>
</div>
</xsl:template>
<xsl:template match="data/Object/Property" mode="desc">
<p>
<xsl:value-of select="."/>
</p>
</xsl:template>
</xsl:stylesheet>
transforms the input
<page>
<elements>
<element name="bids">
<data>
<Object Id="id1">
<Property Name="NAME" Order="0">Paris</Property>
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id1">
<Property Name="NAME" Order="0">Lion</Property>
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id2">
<Property Name="NAME" Order="0">Berlin</Property>
<Property Name="COUNTRY">Germany</Property>
<Property Name="WWW" >http://germany.gr</Property>
</Object>
</data>
</element>
</elements>
</page>
into
<select>
<option value="id1">France</option>
<option value="http://germany.gr">Germany</option></select><div id="city_id1">
<p>Paris</p>
<p>Lion</p>
</div>
[edit]
Here is an adapted stylesheet that uses a different key to try to implement the changed requirements:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output method="html" indent="yes"/>
<xsl:key name="k1" match="data/Object" use="Property[#Name = 'COUNTRY']"/>
<xsl:template match="data[Object]">
<select>
<xsl:apply-templates select="Object[generate-id() = generate-id(key('k1', Property[#Name = 'COUNTRY'])[1])]"/>
</select>
<xsl:apply-templates select="Object[generate-id() = generate-id(key('k1', Property[#Name = 'COUNTRY'])[1]) and key('k1', Property[#Name = 'COUNTRY'])[2]]" mode="desc"/>
</xsl:template>
<xsl:template match="data/Object[key('k1', Property[#Name = 'COUNTRY'])[2]]">
<option value="{Property[#Name = 'COUNTRY']}">
<xsl:value-of select="Property[#Name = 'COUNTRY']"/>
</option>
</xsl:template>
<xsl:template match="data/Object[not(key('k1', Property[#Name = 'COUNTRY'])[2])]">
<option value="{Property[#Name = 'WWW']}">
<xsl:value-of select="Property[#Name = 'COUNTRY']"/>
</option>
</xsl:template>
<xsl:template match="data/Object" mode="desc">
<div id="{Property[#Name = 'COUNTRY']}">
<xsl:apply-templates select="key('k1', Property[#Name = 'COUNTRY'])" mode="link">
<xsl:sort select="#Name"/>
</xsl:apply-templates>
</div>
</xsl:template>
<xsl:template match="data/Object" mode="link">
<p>
<a href="/index.php?id={#Id}">
<xsl:value-of select="#Name"/>
</a>
</p>
</xsl:template>
</xsl:stylesheet>
When I apply that stylesheet to the input
<?xml version="1.0" encoding="utf-8" ?>
<page>
<elements>
<element>
<data>
<Styles Name="default">
<Style Url="/css.css" Browser="default" Version="default"/>
</Styles>
</data>
</element>
<element type="Digillect.WB.Web.Elements.Site.SiteStructureElement">
<config StartLevel="0" MaxDepth="3" UseItemVisibility="false">
<monikers>
<moniker store="asdasd"/>
</monikers>
</config>
<data ParentPath="/">
</data>
</element>
<element name="bids">
<config>
<Object Id="1b61995a-6e22-4b09-af5f-9a50cdaa7863"/>
<Object Id="baa1d3df-0510-4f68-8a41-1b9b22587134"/>
</config>
<data>
<Object Id="id2" Name="Paris">
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id1" Name="Lion">
<Property Name="COUNTRY">France</Property>
<Property Name="WWW" >http://france.fr</Property>
</Object>
<Object Id="id3" Name="Berlin">
<Property Name="COUNTRY">Germany</Property>
<Property Name="WWW" >http://germany.gr</Property>
</Object>
</data>
</element>
</elements>
</page>
the result is
<select>
<option value="France">France</option>
<option value="http://germany.gr">Germany</option></select><div id="France">
<p>Lion</p>
<p>Paris</p>
</div>
so the option elements are grouped as required (although I couldn't figure out what determines the sort order) and the p elements contain links with the #Id value incorporated.