Cycle through xsl:key, output results, add delimiter - but skipping some results - xslt

I have an xsl:key that I am using with a large text-based XML file, using XSL 3 processed by Saxon.
<xsl:key name="getcommonseg" match="seg[#master-id]" use="#master-id"/>
This key is passed against thousands of elements that look like this:
<seg id="unique_id_here" master-id="master-id_here">
The #master-id is actually one of the #ids that 'groups' a number of <seg> together. For example:
<seg id="1-A" master-id="1-A"/>
<seg id="1-G" master-id="1-A"/>
<seg id="2-G" master-id="2-G"/>
<seg id="10-Y" master-id="1-A"/>
<seg id="5-C"/>
<seg id="6-B" master-id="2-G"/>
<seg id="8-R"/>
<seg id="11-K" master-id="2-G"/>
<seg id="19-D" master-id="1-A"/>
<seg id="22-T" master-id="2-G"/>
[...]
When I am reporting any given <seg>, I use the <xsl:key> to find the rest any related <seg>. For example, if I am positioned on:
<seg id="11-K" master-id="2-G"/>
I apply the key, and then cycle through the returned records, output them separated by a comma, and reject the record that is the current record:
<template match="seg[#commonid]">
<xsl:variable name="masterid" select="./#master-id"/>
<xsl:variable name="currentid" select="./#id"/>
<xsl:value-of select="'Seg' || ./#id || 'corresponds to the following seg id(s): ' "/>
<xsl:for-each select="key('getcommonseg',$masterid)">
<xsl:if test="./#id != $currentid">
<xsl:value-of select="./#id"/>
<xsl:if test="position() != last()">, </xsl:if>
</xsl:if>
</xsl:for-each>
</template>
This outputs:
Seg 11-K corresponds to the following seg id(s): 2-G, 6-B, 22-T
As you can see it doesn't output value '11-K' and adds commas appropriately. However, if the position was:
<seg id="22-T" master-id="2-G"/>
It would output:
Seg 22-T corresponds to the following seg id(s): 2-G, 6-B, 11-K,
...adding the last comma inadvertently.
How can I avoid this problem of the final comma when the last node is skipped/rejected?
Thanks in advance.

As you are using XSLT 2 or 3 you should simply use <xsl:value-of select="(key('getcommonseg', #master-id) except .)/#id" separator=", "/>, instead of "cycling".

Related

How to append string values inside if condition

I am new to XSL.I have an XML as below, If CoverageCode equals -'HomeCoverage' then I have to verify for the next 3 elements of 'roofRestrictionEndt','sidingRestrictionEndt'and 'paintRestrictionEndt' . If 'roofRestrictionEndt' exits and its value is 'Y' then I need to print 'Roof' under the 'results' tag, If 'sidingRestrictionEndt' exists and its value is 'Y' then I need to print 'siding' in case if it exists along with the above one then I need to print 'Roof; siding'. If 'paintRestrictionEndt' exists and its value is 'Y' along with the other 2 elements then I need to print 'Roof; siding; paint'. I tried by declaring variables and wrote If conditions and tried to append values accordingly inside IF condition, but I came to know the declared variables are immutable. In java, we can achieve this by using StringBuffer. Is there any way to achieve this in XSL? Below is XML.
<locationCoverage ID="3">
<coverageCode >HomeCoverage</coverageCode>
<roofRestrictionEndt >Y</roofRestrictionEndt>
<sidingRestrictionEndt>Y</sidingRestrictionEndt>
<paintRestrictionEndt >Y</paintRestrictionEndt>
<locationCoverage>
Results should look like as below
<results>
<result>Roof;siding;paint</result>
</results>
If I have below input XML
<locationCoverage ID="3">
<coverageCode >HomeCoverage</coverageCode>
<roofRestrictionEndt >Y</roofRestrictionEndt>
<paintRestrictionEndt >Y</paintRestrictionEndt>
</locationCoverage>
For the above XML results should look like as below
<results>
<result>Roof;paint</result>
</results>
Appreciate it If anyone helps me with this. Thanks in advance.
If I understand this correctly (which is not at all certain), you want to do something like:
<xsl:template match="locationCoverage[coverageCode='HomeCoverage']">
<xsl:variable name="test-results">
<xsl:if test="roofRestrictionEndt='Y'">Roof </xsl:if>
<xsl:if test="sidingRestrictionEndt='Y'">siding </xsl:if>
<xsl:if test="paintRestrictionEndt='Y'">paint</xsl:if>
</xsl:variable>
<result>
<xsl:value-of select="translate(normalize-space($test-results), ' ', ';')"/>
</result>
</xsl:template>

Inconsistency in nodeset test?

I have an XML with top-level elements in this vein:
<chapter template="one"/>
<chapter template="two"/>
<chapter template="one"/>
<chapter template="one"/>
<chapter template="two"/>
<chapter template="one"/>
I'm processing these elements by looping through them with a choose statement:
<xsl:variable name="layout" select="#template"/>
<xsl:choose>
<xsl:when test="contains($layout, 'one')">
<xsl:call-template name="processChapterOne"/>
</xsl:when>
<xsl:when test="contains($layout, 'two')">
<xsl:call-template name="processChaptertwo"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
This works correctly. But now I'm trying to do some conditional processing, so I'm trying to find the first chapter in the list:
<xsl:when test="count(preceding-sibling::*[($layout = 'one')]) = '0'">
<xsl:call-template name="processChapterOne"/>
</xsl:when>
Here's when things get weird. My test never becomes true: the value of count(...) is 4 for the first chapter in the list, and increments from there. It looks like it counts all of the top-level elements, and not just the ones named 'chapter'.
When I change the code to this:
<xsl:when test="count(preceding-sibling::*[(#template = 'one')]) = '0'">
<xsl:call-template name="processChapterOne"/>
</xsl:when>
it works correctly. So I've replaced a variable with a direct reference. I can't figure out why this would make a difference. What could cause this?
The not working and working cases are actually very different:
Not working: In preceding-sibling::*[$layout = 'one'], $layout is always the same value of one as it was when originally set in the <xsl:variable name="layout" select="#template"/> statement.
Working: In preceding-sibling::*[#template = 'one'], #template varies per the #template attribute value of the varying preceding-sibling context nodes.
*[(#template = 'one')]
Above means: count all nodes where attribute template equals the text one.
*[($layout = 'one')]
Above means: count all nodes where variable layout equals the text one.
I think with the question you raised $layout is not filled with the text one, but it does a xsl:call-template. Maybe something is going wrong here?
Besides that if you don't want to count all nodes but only the chapter nodes. Do this:
chapter[($layout = 'one')]
chapter[(#template = 'one')]

Need to remove Note: from the start of a string using XSLT

I have been tasked with styling XML and so far nearly all of the tags I have been given are either p or ph which just display the contents of the tag on screen and I can style as required
eg:
<p outputclass="LC AStatProv">Council Regulation (EC) No 2201/2003 of 27 November 2003 Concerning Jurisdiction and the Recognition and Enforcement of Judgments in Matrimonial Matters and in Matters of Parental Responsibility, repealing Regulation (EC) No 1347/2000 (Brussels II Revised) (2003) OJ L 338/1, Art 20</p>
will display as:
Council Regulation (EC) No 2201/2003 of 27 November 2003 Concerning Jurisdiction and the Recognition and Enforcement of Judgments in Matrimonial Matters and in Matters of Parental Responsibility, repealing Regulation (EC) No 1347/2000 (Brussels II Revised) (2003) OJ L 338/1, Art 20
Today I have noticed that a new note tag has been used and this is causing "Note: " to be prepended to the beginning of each string eg:
<p outputclass="LC ACourt"><note outputclass="CaseSearchCourt">Court of Appeal</note></p>
will display as:
Note: Re A (Abduction: Interim Directions: Accommodation by Local Authority) [2010] EWCA Civ 586 [2011] 1 FLR 1
The FO produced for this is:
<fo:block><fo:inline font-weight="bold" border-right-width="0pt" border-left-width="0pt">Note: </fo:inline> Court of Appeal</fo:block>
I have tried the following code gleaned from elsewhere on this site but it didn't remove the string I wanted:
<xsl:template match="note">
<note>
<xsl:if test="contains(., 'Note:')">
<xsl:call-template name="remove">
<xsl:with-param name="value" select="."/>
</xsl:call-template>
</xsl:if>
</note>
</xsl:template>
<xsl:template name="remove">
<xsl:param name="value"/>
<xsl:value-of select="concat(substring-before($value, 'Note:'), substring-after($value, 'Note:'))"/>
</xsl:template>
I have tried to see where XSLT gets the "Note: " from but can't find it anywhere so my next thought is to try and pattern match the string and remove "Note: " from the beginning of the string. Is this a good way to go about this and how would I do this?
thanks.
EDIT: Just to summarise my rambling and confusing question.
Where it renders:
Note: Re A (Abduction: Interim Directions: Accommodation by Local Authority) [2010] EWCA Civ 586 [2011] 1 FLR 1
I just want it to render:
Re A (Abduction: Interim Directions: Accommodation by Local Authority) [2010] EWCA Civ 586 [2011] 1 FLR 1
EDIT 2 and the answer
My initial investigations were based upon grepping for Note: which wasn't being found. I have though found the following:
<xsl:template match="*[contains(#class,' topic/note ')]">
and then commented out the following lines:
<!--<xsl:text>Note</xsl:text>-->
</xsl:otherwise>
</xsl:choose>
<!--<xsl:text>: </xsl:text>-->
</fo:inline>
<xsl:text> </xsl:text>
<xsl:apply-templates/>
And I have got rid of the "Note: ".
EDIT 3:
This didn't actually work as it created an opening block but didn't close it so I ended up removing the entire <xsl:template match="*[contains(#class,' topic/note ')]"> as we have no need for any type of notes in our system
This didn't actually work as it created an opening block but didn't close it so I ended up removing the entire <xsl:template match="*[contains(#class,' topic/note ')]"> as we have no need for any type of notes in our system

XSLT line counter - is it that hard?

I have cheated every time I've needed to do a line count in XSLT by using JScript, but in this case I can't do that. I simply want to write out a line counter throughout an output file. This basic example has a simple solution:
<xsl:for-each select="Records/Record">
<xsl:value-of select="position()"/>
</xsl:for-each>
Output would be:
1
2
3
4
etc...
But what if the structure is more complex with nested foreach's :
<xsl:for-each select="Records/Record">
<xsl:value-of select="position()"/>
<xsl:for-each select="Records/Record">
<xsl:value-of select="position()"/>
</xsl:for-each>
</xsl:for-each>
Here, the inner foreach would just reset the counter (so you get 1, 1, 2, 3, 2, 1, 2, 3, 1, 2 etc). Does anyone know how I can output the position in the file (ie. a line count)?
While it is quite impossible to mark the line numbers for the serialization of an XML document (because this serialization per se is ambiguous), it is perfectly possible, and easy, to number the lines of regular text.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:call-template name="numberLines"/>
</xsl:template>
<xsl:template name="numberLines">
<xsl:param name="pLastLineNum" select="0"/>
<xsl:param name="pText" select="."/>
<xsl:if test="string-length($pText)">
<xsl:value-of select="concat($pLastLineNum+1, ' ')"/>
<xsl:value-of select="substring-before($pText, '
')"/>
<xsl:text>
</xsl:text>
<xsl:call-template name="numberLines">
<xsl:with-param name="pLastLineNum"
select="$pLastLineNum+1"/>
<xsl:with-param name="pText"
select="substring-after($pText, '
')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<t>The biggest airlines are imposing "peak travel surcharges"
this summer. In other words, they're going to raise fees
without admitting they're raising fees: Hey, it's not a $30
price hike. It's a surcharge! This comes on the heels of
checked-baggage fees, blanket fees, extra fees for window
and aisle seats, and "snack packs" priced at exorbitant
markups. Hotels in Las Vegas and elsewhere, meanwhile, are
imposing "resort fees" for the use of facilities (in other
words, raising room rates without admitting they're
raising room rates). The chiseling dishonesty of these
tactics rankles, and every one feels like another nail in
the coffin of travel as something liberating and
pleasurable.
</t>
produces the desired line-numbering:
1 The biggest airlines are imposing "peak travel surcharges"
2 this summer. In other words, they're going to raise fees
3 without admitting they're raising fees: Hey, it's not a $30
4 price hike. It's a surcharge! This comes on the heels of
5 checked-baggage fees, blanket fees, extra fees for window
6 and aisle seats, and "snack packs" priced at exorbitant
7 markups. Hotels in Las Vegas and elsewhere, meanwhile, are
8 imposing "resort fees" for the use of facilities (in other
9 words, raising room rates without admitting they're
10 raising room rates). The chiseling dishonesty of these
11 tactics rankles, and every one feels like another nail in
12 the coffin of travel as something liberating and
13 pleasurable.
A line in an XML file is not really the same as an element. In your first example you don't really count the lines - but the number of elements.
An XML file could look like this:
<cheeseCollection>
<cheese country="Cyprus">Gbejna</cheese><cheese>Liptauer</cheese><cheese>Anari</cheese>
</cheeseCollection>
Or the exact same XML file can look like this:
<cheeseCollection>
<cheese
country="Cyprus">Gbejna</cheese>
<cheese>Liptauer</cheese>
<cheese>Anari</cheese>
</cheeseCollection>
which the XSLT will interpet exactly the same - it will not really bother with the line breaks.
Therefore it's hard to show line numbers in the way you want using XSLT - it's not really meant for for that kind of parsing.
Someone correct me if I'm wrong, but I'd say you would need Javascript or some other scripting language to do what you want.
Thanks for the responses guys - yup you're totally correct, some external function is the only way to get this behaviour in XSLT. For those searching, this is how I did this when using a compiled transform in .Net 3.5:
Create a helper class for your function(s)
/// <summary>
/// Provides functional support to XSLT
/// </summary>
public class XslHelper
{
/// <summary>
/// Initialise the line counter value to 1
/// </summary>
Int32 counter = 1;
/// <summary>
/// Increment and return the line count
/// </summary>
/// <returns></returns>
public Int32 IncrementCount()
{
return counter++;
}
}
Add an instance to an args list for XSLT
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(XmlReader.Create(s));
XsltArgumentList xslArg = new XsltArgumentList();
XslHelper helper = new XslHelper();
xslArg.AddExtensionObject("urn:helper", helper);
xslt.Transform(xd.CreateReader(), xslArg, writer);
Use it in you XSLT
Put this in the stylesheet declaration element:
xmlns:helper="urn:helper"
Then use like so:
<xsl:value-of select="helper:IncrementCount()" />
Generally, position() is referring to the number of the current node relative to the entire batch of nodes that is being processed currently.
With your "nested for-each" example, consecutive numbering can easily be achieved when you stop nesting for-each constructs and just select all desired elements at once.
With this XML:
<a><b><c/><c/></b><b><c/></b></a>
a loop construct like this
<xsl:for-each "a/b">
<xsl:value-of select="position()" />
<xsl:for-each "c">
<xsl:value-of select="position()" />
</xsl:for-each>
</xsl:for-each>
will result in
11221
bccbc // referred-to nodes
but you could simply do this instead:
<xsl:for-each "a/b/c">
<xsl:value-of select="position()" />
</xsl:for-each>
and you would get
123
ccc // referred-to nodes

Walk/loop through an XSL key: how?

Is there a way to walk-through a key and output all the values it contains?
<xsl:key name="kElement" match="Element/Element[#idref]" use="#idref" />
I though of it this way:
<xsl:for-each select="key('kElement', '.')">
<li><xsl:value-of select="." /></li>
</xsl:for-each>
However, this does not work. I simply want to list all the values in a key for testing purposes.
The question is simply: how can this be done?
You can't. That's not what keys are for.
You can loop through every element in a key using a single call to key() if and only if the key of each element is the same.
If you need to loop over everything the key is defined over, you can use the expression in the match="..." attribute of your <key> element.
So if you had a file like this:
<root>
<element name="Bill"/>
<element name="Francis"/>
<element name="Louis"/>
<element name="Zoey"/>
</root>
And a key defined like this:
<xsl:key name="survivors" match="element" use="#name"/>
You can loop through what the key uses by using the contents of its match attribute:
<xsl:for-each select="element">
<!-- stuff -->
</xsl:for-each>
Alternatively, if each element had something in common:
<root>
<element name="Bill" class="survivor"/>
<element name="Francis" class="survivor"/>
<element name="Louis" class="survivor"/>
<element name="Zoey" class="survivor"/>
</root>
Then you could define your key like this:
<xsl:key name="survivors" match="element" use="#class"/>
And iterate over all elements like this:
<xsl:for-each select="key('survivors', 'survivor')">
<!-- stuff -->
</xsl:for-each>
Because each element shares the value "survivor" for the class attribute.
In your case, your key is
<xsl:key name="kElement" match="Element/Element[#idref]" use="#idref" />
So you can loop through everything it has like this:
<xsl:for-each select="Element/Element[#idref]">
<!-- stuff -->
</xsl:for-each>
You CAN create a key to use for looping - if you simply specify a constant in the use attribute of the key element:
<xsl:key name="survivors" match="element" use="'all'"/>
Then you can loop over all elements in the following way:
<xsl:for-each select="key('survivors','all')">
...
</xsl:for-each>
Or count them:
<xsl:value-of select="count(key('survivors','all'))"/>
Note that the constant can be any string or even a number - but 'all' reads well.
However, you cannot use this key to lookup information about the individual entries (because they all have the same key).
In other words there are two types of possible keys:
"lookup keys" = standard keys with varying indexes in the use attribute
"looping keys" = keys with a constant in the use attribute
I do not know how efficient this method is to execute, it does however make the maintenance of the XSL more efficient by avoiding repetition of the same (potentially very complex) XPath expression throughout the XSL code.
Rather than think of the XSL keys in programming language terms, think of them as record sets of SQL. That will give a better understanding. For a given key index created as
<xsl:key name="paths" match="path" use="keygenerator()">
it can be "iterated"/"walk-through" as below
<xsl:for-each select="//path[generate-id()=generate-id(key('paths',keygenerator())[1])]">
To understand this magic number [1], let s go through the below example :
Consider this XML snippet
<root>
<Person>
<name>Johny</name>
<date>Jan10</date>
<cost itemID="1">34</cost>
<cost itemID="1">35</cost>
<cost itemID="2">12</cost>
<cost itemID="3">09</cost>
</Person>
<Person>
<name>Johny</name>
<date>Jan09</date>
<cost itemID="1">21</cost>
<cost itemID="1">41</cost>
<cost itemID="2">11</cost>
<cost itemID="2">14</cost>
</Person>
</root>
transformed using this XSL.
<xsl:for-each select="*/Person">
<personrecords>
<xsl:value-of select="generate-id(.)" />--
<xsl:value-of select="name"/>--
<xsl:value-of select="date"/>--
</personrecords>
</xsl:for-each>
<xsl:for-each select="*/*/cost">
<costrecords>
<xsl:value-of select="generate-id(.)" />--
<xsl:value-of select="../name"/>--
<xsl:value-of select="../date"/>--
<xsl:value-of select="#itemID"/>--
<xsl:value-of select="text()"/>
</costrecords>
</xsl:for-each>
The above XSL transformation lists the unique id of the Person nodes and the cost nodes in the form of idpxxxxxxx as the result below shows.
1. <personrecords>idp2661952--Johny--Jan10-- </personrecords>
2. <personrecords>idp4012736--Johny--Jan09-- </personrecords>
3. <costrecords>idp2805696--Johny-- Jan10-- 1-- 34</costrecords>
4. <costrecords>idp4013568--Johny-- Jan10-- 1-- 35</costrecords>
5. <costrecords>idp2808192--Johny-- Jan10-- 2-- 12</costrecords>
6. <costrecords>idp2808640--Johny-- Jan10-- 3-- 09</costrecords>
7. <costrecords>idp2609728--Johny-- Jan09-- 1-- 21</costrecords>
8. <costrecords>idp4011648--Johny-- Jan09-- 1-- 41</costrecords>
9. <costrecords>idp2612224--Johny-- Jan09-- 2-- 11</costrecords>
10.<costrecords>idp2610432--Johny-- Jan09-- 2-- 14</costrecords>
Let us create a key on the cost records using a combination of name and itemID values.
<xsl:key name="keyByNameItem" match="cost" use="concat(../name, '+', #itemID)"/>
Manually looking at the XML, the number of unique keys for the above would be three : Johny+1, Johny+2 and Johny+3.
Now lets test out this key by using the snippet below.
<xsl:for-each select="*/*/cost">
<costkeygroup>
<xsl:value-of select="generate-id(.)" />--
(1)<xsl:value-of select="generate-id(key('keyByNameItem',concat(../name, '+', #itemID) )[1] ) " />--
(2)<xsl:value-of select="generate-id(key('keyByNameItem',concat(../name, '+', #itemID) )[2] ) " />--
(3)<xsl:value-of select="generate-id(key('keyByNameItem',concat(../name, '+', #itemID) )[3] ) " />--
(4)<xsl:value-of select="generate-id(key('keyByNameItem',concat(../name, '+', #itemID) )[4] ) " />
</costkeygroup>
</xsl:for-each>
And here is the result:
1. <costkeygroup>idp2805696-- (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648</costkeygroup>
2. <costkeygroup>idp4013568-- (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648</costkeygroup>
3. <costkeygroup>idp2808192-- (1)idp2808192-- (2)idp2612224-- (3)idp2610432-- (4)</costkeygroup>
4. <costkeygroup>idp2808640-- (1)idp2808640-- (2)-- (3)-- (4)</costkeygroup>
5. <costkeygroup>idp2609728-- (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648</costkeygroup>
6. <costkeygroup>idp4011648-- (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648</costkeygroup>
7. <costkeygroup>idp2612224-- (1)idp2808192-- (2)idp2612224-- (3)idp2610432-- (4)</costkeygroup>
8. <costkeygroup>idp2610432-- (1)idp2808192-- (2)idp2612224-- (3)idp2610432-- (4)</costkeygroup>
Our interest is in trying to understand the importance of [1],[2], [3],[4]. In our case, the keygenerator is concat(../name, '+', #itemID).
For a given key, [1] refers to the first occurence of a node that satisfies the keygenerator. Similarly [2] refers to the second occurence of a node that satisfies the keygenerator. Thus [2], [3],[4], etc. are all nodes that satisfy the same key, and thus can be considered duplicates for the given key. The number of duplicates depends on the input XML. Thus:
Key Johny+1 satisfies 4 nodes (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648
Key Johny+2 satisfies 3 nodes (1)idp2808192-- (2)idp2612224-- (3)idp2610432-- (4)
Key Johny+3 satisfies 1 node (1)idp2808640-- (2)-- (3)-- (4)
Thus we see that ALL 8 cost nodes of the XML can be accessed through the key.
Here is a image that combines the transformation results to help better understand.
The red squares indicate the matching nodes for Johny+1. The green squares indicate the matching nodes for Johny+3. Match the idpxxxxxxx values in <costkeygroup> to the values in <costrecords>. The <costrecords> help map the idpxxxxxxx values to the source XML.
The takeaway is that,
an XSL key does not filter or eliminate nodes. All nodes including duplicates can be accessed through the key. Thus when we say "walk through" of the key, there is no concept of a resultant subset of nodes from the original set of nodes made available to the key for processing.
To "walk through" only unique nodes of the key in the above example, use
<xsl:for-each select="*/*/workTime[generate-id()=generate-id(key('keyByNameItem', concat(../name, '+', #itemID) )[1] ) ] ">
[1] signifies that the first record for a given key value is denoted as the unique record. [1] is almost always used because there will exist at least one node that satisfies a given key value. If we are sure that there will be a minimum of 2 records to satisfy each key value in the key, we can go ahead and use [2] to identify the second record in the record set as the unique record.
P.S The words nodes / records / elements are used interchangeably.
There is no way to walk-through the keys, although we can output all the values it contains. In XSLT2 it is quite easier than in XSLT1 (e.g., using fn:generate-id according to the previous answer).
Using fn:distinct-values
<xsl:variable name="e" select="."/>
<xsl:for-each select="distinct-values(Element/Element[#idref]/#idref)">
<li key="{.}"><xsl:value-of select="key('kElement', ., $e )" /></li>
</xsl:for-each>
Using xsl:for-each-group
<xsl:for-each-group select="Element/Element[#idref]" group-by="#idref">
<li key="{current-grouping-key()}"><xsl:value-of select="current-group()" /></li>
</xsl:for-each-group>