I am trying to compare two xml using xsl:key, but not sure how to print unmatched keys. In this scenario I am keying b.xml and comparing with a.xml, but it does not print unmatched keys from b.xml.
a.xml
<root>
<metas>
<meta>
<name>x</name>
<value>0</value>
</meta>
<meta>
<name>y</name>
<value>1</value>
</meta>
<meta>
<name>z</name>
<value>1</value>
</meta>
</metas>
b.xml
<root>
<metas>
<info>
<name>a</name>
<value>0</value>
</info>
<info>
<name>y</name>
<value>1</value>
</info>
<info>
<name>z</name>
<value>1</value>
</info>
</metas>
Desired output:
Table
a.xml b.xml
name missing in a.xml a
value missing in a.xml 0
name x missing in b.xml
value 0 missing in b.xml
name y y
value 1 1
name z z
value 1 1
My xsl;
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:param name="uri" as="xs:string" select="'b.xml'"/>
<xsl:param name="b" as="document-node()" select="doc($uri)"/>
<xsl:key name="bCompare" match="root/metas/info" use="name"/>
<xsl:template match="/">
<html>
<head>
<title>Test</title>
</head>
<body>
<table>
<tr>
<td></td>
<th>a.xml</th>
<th>b.xml</th>
</tr>
<xsl:apply-templates/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="metas">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="meta">
<xsl:variable name="compare" select="key('bCompare', name, $b)"/>
<tr>
<th>Name</th>
<td>
<xsl:value-of select="name"/>
</td>
<td>
<xsl:value-of select="$compare/name"/>
<xsl:if test="empty($compare/name)">missing in b.xml</xsl:if>
</td>
</tr>
<tr>
<th>Value</th>
<td>
<xsl:value-of select="value"/>
</td>
<td>
<xsl:value-of select="$compare/value"/>
<xsl:if test="empty($compare/value)">missing in b.xml</xsl:if>
</td>
</tr>
</xsl:template>
You need to define a key for the other direction as well, i.e. <xsl:key name="aCompare" match="root/metas/meta" use="name"/>, then you need to make sure you process those elements of the second document with e.g. <xsl:apply-templates/><xsl:variable name="main-doc" select="/"/><xsl:apply-templates select="$b//info/meta[not(name = key('bCompare', $main-doc//meta/name, $main-doc))]"/> and then you need a template
<xsl:template match="info">
<xsl:variable name="compare" select="key('aCompare', name, $main-doc)"/>
<tr>
<th>Name</th>
<td>
<xsl:value-of select="$compare/name"/>
<xsl:if test="empty($compare/name)">missing in a.xml</xsl:if>
</td>
<td>
<xsl:value-of select="name"/>
</td>
</tr>
<tr>
<th>Value</th>
<td>
<xsl:value-of select="$compare/value"/>
<xsl:if test="empty($compare/value)">missing in a.xml</xsl:if>
</td>
<td>
<xsl:value-of select="value"/>
</td>
</tr>
</xsl:template>
Related
My XML input:
<result>
<objects name="wf1">
<object>
<qname>wf</qname>
<object_name>Nr 1</object_name>
<person_name>Anton</person_name>
</object>
<object>
<qname>wf</qname>
<object_name>Nr 2</object_name>
<person_name>Ben</person_name>
</object>
</objects>
<objects name="wf2">
<object>
<qname>wf</qname>
<object_name>Nr 2</object_name>
<person_name>Chris</person_name>
</object>
<object>
<qname>wf</qname>
<object_name>Nr 3</object_name>
<person_name>Dirk</person_name>
</object>
</objects>
</result>
Things to note
There are two blocks with information: wf1 and wf2
Both have an object with object_name = 'Nr 2'
All objects have qname = 'wf'
My XSLT:
<xsl:template match="/">
<xsl:call-template name="output_html" />
</xsl:template>
<xsl:template name="output_html">
<html>
<body >
<table>
<tr>
<th>Object name</th>
<th>Person name</th>
</tr>
<xsl:apply-templates select="result/objects/object[qname='wf']" />
</table>
</body>
</html>
</xsl:template>
<xsl:template match="object[*]">
<tr align="center" valign="top">
<td><xsl:value-of select="object_name"/></td>
<td><xsl:value-of select="person_name"/></td>
</tr>
</xsl:template>
Output:
Object name
Person name
Nr 1
Anton
Nr 2
Ben
Nr 2
Chris
Nr 3
Dirk
My wish:
I would like to see unique object names. If an object with a certain name appears in both wf1 and wf2 then only the one from wf2 should be shown.
So the desired output would be:
Object name
Person name
Nr 1
Anton
Nr 2
Chris
Nr 3
Dirk
The information "Ben" gets lost. That is fine.
Does anybody have ideas about how to achieve that in XSLT 1.0?
In XSLT 1.0, it would be best to adapt the Muenchian method to the current problem:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="obj-by-name" match="object" use="object_name" />
<xsl:template match="/result">
<html>
<body >
<table>
<!-- header -->
<tr>
<th>Object name</th>
<th>Person name</th>
</tr>
<!-- for each distinct object_name -->
<xsl:for-each select="objects/object[count(. | key('obj-by-name', object_name)[1]) = 1]">
<tr>
<td>
<xsl:value-of select="object_name"/>
</td>
<td>
<!-- sort the group with wf2 on top -->
<xsl:for-each select="key('obj-by-name', object_name)">
<xsl:sort select="number(../#name='wf2')" data-type="number" order="descending"/>
<xsl:if test="position()=1">
<xsl:value-of select="person_name"/>
</xsl:if>
</xsl:for-each>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
If <objects name="wf2"> will always come after <objects name="wf1">, then you can shorten this to:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="obj-by-name" match="object" use="object_name" />
<xsl:template match="/result">
<html>
<body >
<table>
<!-- header -->
<tr>
<th>Object name</th>
<th>Person name</th>
</tr>
<!-- for each distinct object_name -->
<xsl:for-each select="objects/object[count(. | key('obj-by-name', object_name)[1]) = 1]">
<tr>
<td>
<xsl:value-of select="object_name"/>
</td>
<td>
<xsl:value-of select="key('obj-by-name', object_name)[last()]/person_name"/>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
I have a xml below
<Report>
<rl>
<id>12345;12346</id>
<activity>a2/a3</activity>
<result>r2/r3</result>
<operator>test</operator>
<timestamp>12/18/2014 3:51:19 PM</timestamp>
<quantity>2</quantity>
</rl>
<rl>
<id>22345;22346</id>
<activity>a3/a4</activity>
<result>r3/r4</result>
<operator>test</operator>
<timestamp>12/18/2014 3:51:19 PM</timestamp>
<quantity>2</quantity>
</rl>
</Report>
and for my xsl,
<table border="1" style="border-width: 1px" width="90%" bordercolor="#C0C0C0" align="center">
<tr>
<th width="5%" align="center">
<font color="#000000" face="Verdana" size="3">Index</font>
</th>
<th width="15%" align="center">
<font color="#000000" face="Verdana" size="3">ID A:</font>
</th>
<th width="15%" align="center">
<font color="#000000" face="Verdana" size="3">ID B:</font>
</th>
</tr>
<xsl:for-each select="Report/rl">
<tr height="25">
<td width="5%" align="center" >
<font color="#000000" face="Verdana" size="2">
<xsl:value-of select="position()" />
</font>
</td>
<td align="center">
<font color="#000000" face="Verdana" size="2">
<xsl:value-of select="idA" />
</font>
</td>
<td align="center">
<font color="#000000" face="Verdana" size="2">
<xsl:value-of select="idB" />
</font>
</td>
</tr>
</xsl:for-each>
</table>
For 1st rl, there is 12345:12346 for tag, I want to split them into 12345 and 12346 and show them in the 'idA' and 'idB'. How should I do that?
My xslt version is 1.0.
Assuming there are always exactly two values, separated by a semicolon, use:
<xsl:value-of select="substring-before(id, ';')"/>
to populate the idA cell, and:
<xsl:value-of select="substring-after(id, ';')"/>
to populate the idB cell.
Added:
For the same example as posted, can you elaborate more about the
'recursive named template'?
The solution using a recursive named template would look something like this:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/Report">
<table border="1">
<tr>
<th>Index</th>
<th>ID A</th>
<th>ID B</th>
</tr>
<xsl:apply-templates select="rl"/>
</table>
</xsl:template>
<xsl:template match="rl">
<tr>
<td>
<xsl:value-of select="position()" />
</td>
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="id"/>
</xsl:call-template>
</tr>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="text"/>
<xsl:param name="delimiter" select="';'"/>
<td>
<xsl:value-of select="substring-before(concat($text, $delimiter), $delimiter)"/>
</td>
<xsl:if test="contains($text, $delimiter)">
<!-- recursive call -->
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="substring-after($text, $delimiter)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Note that for the header we assume that the number of columns is known beforehand. Otherwise you'd have to use a similar recursive template to generate the header cells too.
Check this example
<?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"/>
<xsl:template match="*">
<xsl:for-each select="rl">
<node>
<idA>
<xsl:value-of select="substring-before(id,';')"/>
</idA>
<idB>
<xsl:value-of select="substring-after(id,';')"/>
</idB>
</node>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Below is my input xml
<Record>
<Header>
<field1/>
</Header>
<Body>
<firstname>x1</firstname>
<lastname>y1</lastname>
<company>Test1</company>
<Body>
<Body>
<firstname>x2</firstname>
<lastname>y2</lastname>
<company></company>
<Body>
<Body>
<firstname>x3</firstname>
<lastname>y3</lastname>
<company>Test2</company>
<Body>
</Record>
I am trying to loop through body and check if the company value is blank,to output the corresponding first name and last name.This whole output,I am mapping to DATA on the target using xslt mapper.Can someone help me with the below code which is not working
<?xml version='1.0' ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns0="Namespace">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="NAME" select="/RecordSet/Body"/>
<xsl:variable name="break"><br></xsl:variable>
<xsl:variable name="tableB"><table Border="1" BorderColor="#000000" cellpadding="4" cellspacing="0" ></xsl:variable>
<xsl:variable name="tableE"></table></xsl:variable>
<xsl:variable name="trB"><tr></xsl:variable>
<xsl:variable name="trE"></tr></xsl:variable>
<xsl:variable name="tdB"><td></xsl:variable>
<xsl:variable name="tdE"></td></xsl:variable>
<xsl:variable name="nbsp"> </xsl:variable>
<xsl:variable name="thB"><tr BGCOLOR="#CCCCCC"></xsl:variable>
<xsl:template match="/">
<DATA>
<xsl:value-of select="$tableB"/>
<xsl:value-of select="$thB"/>
<xsl:value-of select="$tdB"/><B>FirstName</B>
<xsl:value-of select="$nbsp"/>
<xsl:value-of select="$tdE"/>
<xsl:value-of select="$tdB"/><B>LASTNAME </B>
<xsl:value-of select="$nbsp"/>
<xsl:value-of select="$tdE"/>
<xsl:value-of select="$trE"/>
<xsl:value-of select="$trB"/>
<xsl:for-each select="$NAME/Body[string-length(company) > 0]">
<xsl:value-of select="$tdB"/>
<xsl:value-of select="$MT_NAME/firstname"/>
<xsl:value-of select="$nbsp"/>
<xsl:value-of select="$tdE"/>
<xsl:value-of select="$tdB"/>
<xsl:value-of select="$MT_NAME/lastname"/>
<xsl:value-of select="$nbsp"/>
<xsl:value-of select="$tdE"/>
<xsl:if test="position() mod 2 = 0">
<xsl:value-of select="$trE"/>
</xsl:if>
</xsl:for-each>
<xsl:value-of select="$tableE"/>
</DATA>
The output should be
|FIRSTNAME|LASTNAME|
| X2 | Y2 |
The provided code doesn't produce HTML at all -- it produces strings -- one-dimensional text.
Also, AFAIK, DATA isn't an HTML element.
Also, the provided "XML" is severely malformed.
Here is an example how to produce an HTML table with XSLT:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<table>
<thead>
<tr>
<td>First Name</td>
<td>Last Name</td>
<td>Company</td>
</tr>
</thead>
<xsl:apply-templates/>
</table>
</xsl:template>
<xsl:template match="Body">
<tr>
<xsl:apply-templates select="*"/>
</tr>
</xsl:template>
<xsl:template match="Body/*">
<td> </td>
</xsl:template>
<xsl:template match="Body/*[normalize-space()]">
<td><xsl:value-of select="."/></td>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document (the provided severely malformed text -- corrected):
<Record>
<Header>
<field1/>
<Body>
<firstname>x1</firstname>
<lastname>y1</lastname>
<company>Test1</company>
</Body>
<Body>
<firstname>x2</firstname>
<lastname>y2</lastname>
<company></company>
</Body>
<Body>
<firstname>x3</firstname>
<lastname>y3</lastname>
<company>Test2</company>
</Body>
</Header>
</Record>
A meaningful and sensible HTML table is produced:
<table>
<thead>
<tr>
<td>First Name</td>
<td>Last Name</td>
<td>Company</td>
</tr>
</thead>
<tr>
<td>x1</td>
<td>y1</td>
<td>Test1</td>
</tr>
<tr>
<td>x2</td>
<td>y2</td>
<td> </td>
</tr>
<tr>
<td>x3</td>
<td>y3</td>
<td>Test2</td>
</tr>
</table>
I have to take a guess at your requirements as they are not very well explained.
This XSLT 1.0 style-sheet...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="/">
<DATA>
<table Border="1" BorderColor="#000000" cellpadding="4" cellspacing="0">
<tr BGCOLOR="#CCCCCC">
<th>First name</th>
<th>Last name</th>
<th>Company</th>
</tr>
<xsl:apply-templates select="*/Body" />
</table>
</DATA>
</xsl:template>
<xsl:template match="Body">
<tr>
<td><xsl:value-of select="firstname" /></td>
<td><xsl:value-of select="lastname" /></td>
<td>
<xsl:choose>
<xsl:when test="company!=''" >
<xsl:value-of select="company" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(firstname,' ',lastname)" />
</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
... when applied on this document...
<Record>
<Header>
<field1/>
</Header>
<Body>
<firstname>x1</firstname>
<lastname>y1</lastname>
<company>Test1</company>
</Body>
<Body>
<firstname>x2</firstname>
<lastname>y2</lastname>
<company/>
</Body>
<Body>
<firstname>x3</firstname>
<lastname>y3</lastname>
<company>Test2</company>
</Body>
</Record>
...will yield...
<DATA>
<table Border="1" BorderColor="#000000" cellpadding="4" cellspacing="0">
<tr BGCOLOR="#CCCCCC">
<th>First name</th>
<th>Last name</th>
<th>Company</th>
</tr>
<tr>
<td>x1</td>
<td>y1</td>
<td>Test1</td>
</tr>
<tr>
<td>x2</td>
<td>y2</td>
<td>x2 y2</td>
</tr>
<tr>
<td>x3</td>
<td>y3</td>
<td>Test2</td>
</tr>
</table>
</DATA>
Note
Notice that the middle record has a cell value of 'x2 y2' for Company as per stated requirements when the input Company is empty or missing.
Update
The input document is still badly malformed. In consideration of the OP's updated requirements, this XSLT 1.0 style-sheet...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="/">
<DATA>
<table Border="1" BorderColor="#000000" cellpadding="4" cellspacing="0">
<tr BGCOLOR="#CCCCCC">
<th>First name</th>
<th>Last name</th>
</tr>
<xsl:apply-templates select="*/Body[company='']" />
</table>
</DATA>
</xsl:template>
<xsl:template match="Body">
<tr>
<td><xsl:value-of select="firstname" /></td>
<td><xsl:value-of select="lastname" /></td>
</tr>
</xsl:template>
</xsl:stylesheet>
...when applied to the same input document as before, yields...
<DATA>
<table Border="1" BorderColor="#000000" cellpadding="4" cellspacing="0">
<tr BGCOLOR="#CCCCCC">
<th>First name</th>
<th>Last name</th>
</tr>
<tr>
<td>x2</td>
<td>y2</td>
</tr>
</table>
</DATA>
I have these trees, one with this structure /cars/car and the second /maker/cars/car. The first one has a reference to the id of the second list of cars.
<xsl:template match="t:cars/t:car">
<tr>
<td>
<xsl:if test="position()=1">
<b><xsl:value-of select="../#name"/><xsl:text> </xsl:text></b>
</xsl:if>
</td>
</tr>
I have this, it was filled in with a for loop I learn after a bit that I could't do it.
This is what it was before:
<xsl:template match="t:cars/t:car">
<tr>
<td>
<xsl:if test="position()=1">
<b><xsl:value-of select="../#name"/><xsl:text> </xsl:text></b>
</xsl:if>
<xsl:for-each select="/t:root/t:maker/t:car">
<xsl:if test="t:root/t:maker/#id = #ref">
<xsl:value-of select="#title"/>
</xsl:if>
</xsl:for-each>
</td>
</tr>
sample:
auto>
<maker type="toyota">
<car name="prius" id="1"/>
</maker>
<cars name="My Collection">
<car ref="1" />
</cars>
This simple transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kCarById" match="maker/car" use="#id"/>
<xsl:template match="/*">
<table>
<xsl:apply-templates/>
</table>
</xsl:template>
<xsl:template match="cars/car">
<tr>
<td>
<b>
<xsl:value-of select="key('kCarById', #ref)/#name"/>
</b>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document (the provided one, just extended a little):
<auto>
<maker type="toyota">
<car name="prius" id="1"/>
</maker>
<maker type="honda">
<car name="accord" id="2"/>
</maker>
<maker type="benz">
<car name="mercedes" id="3"/>
</maker>
<cars name="My Collection">
<car ref="2" />
<car ref="3" />
</cars>
</auto>
produces the wanted, correct result:
<table>
<tr>
<td>
<b>accord</b>
</td>
</tr>
<tr>
<td>
<b>mercedes</b>
</td>
</tr>
</table>
Explanation: Appropriate use of keys.
My question is a bit similar to XML to HTML table with XSLT .
I have a dictionary defined as follows in XML:
<dictionary>
<languages>
<language>en</language>
<language>ja</language>
</languages>
<entries>
<entry id="1">
<en>Test</en>
<ja>テスト</ja>
</entry>
<entry id="2">
<en>Test2</en>
<ja>テスト2</en>
</entry>
</entries>
</dictionary>
And I would like the following output in XHTML:
<table>
<thead>
<tr>
<th>en</th>
<th>ja</th>
</tr>
</thead>
<tbody>
<tr>
<td>Test</td>
<td>テスト</td>
</tr>
<tr>
<td>Test2</td>
<td>テスト2</td>
</tr>
</tbody>
</table>
I adapted the answer from XML to HTML table with XSLT as follows:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="//dictionary/entries">
<table><xsl:apply-templates select="entry"/></table>
</xsl:template>
<xsl:template match="entry[1]">
<thead><tr><xsl:apply-templates select="*" mode="header"/></tr></thead>
<xsl:call-template name="standardRow"/>
</xsl:template>
<xsl:template match="entry" name="standardRow">
<tbody><tr><xsl:apply-templates select="*"/></tr></tbody>
</xsl:template>
<xsl:template match="entry/*">
<td><xsl:apply-templates select="node()"/></td>
</xsl:template>
<xsl:template match="entry/*" mode="header">
<th><xsl:value-of select="name()"/></th>
</xsl:template>
</xsl:stylesheet>
The thing is that I might have inputs as follows:
<dictionary>
<languages>
<language>en</language>
<language>ja</language>
<language>id</language>
</languages>
<entries>
<entry id="1">
<en>Test</en>
<ja>テスト</ja>
</entry>
<entry id="2">
<ja>テスト2</ja>
<en>Test2</en>
<id>uji2</id>
</entry>
</entries>
</dictionary>
As you might have understood, XSLT takes the first entry node to define the column names and the column id is not generated. Moreover, if the language order is changed in entry the <td> do not appear in order.
With the input above, I would like the following output:
<table>
<thead>
<tr>
<th>en</th>
<th>ja</th>
<th>id</th>
</tr>
</thead>
<tbody>
<tr>
<td>Test</td>
<td>テスト</td>
<td></td>
</tr>
<tr>
<td>Test2</td>
<td>テスト2</td>
<td>Uji2</td>
</tr>
</tbody>
</table>
This is my first time using XSLT and I do not really know how I could do this. I guess I could use the languages node. Please note that the XML input format is flexible and I would welcome any suggestions even if I need to change the format.
Here is a sample stylesheet:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="html" indent="yes"/>
<xsl:key name="k1" match="entry/*" use="concat(generate-id(..), '|', local-name())"/>
<xsl:variable name="languages" select="/dictionary/languages/language"/>
<xsl:template match="dictionary">
<xsl:apply-templates select="entries"/>
</xsl:template>
<xsl:template match="entries">
<table>
<thead>
<tr>
<xsl:apply-templates select="$languages" mode="header"/>
</tr>
</thead>
<tbody>
<xsl:apply-templates/>
</tbody>
</table>
</xsl:template>
<xsl:template match="language" mode="header">
<th>
<xsl:value-of select="."/>
</th>
</xsl:template>
<xsl:template match="entry">
<tr>
<xsl:apply-templates select="$languages">
<xsl:with-param name="entry" select="current()"/>
</xsl:apply-templates>
</tr>
</xsl:template>
<xsl:template match="language">
<xsl:param name="entry"/>
<td>
<xsl:value-of select="key('k1', concat(generate-id($entry), '|', .))"/>
</td>
</xsl:template>
</xsl:stylesheet>