Reputation: 59
I have have two keys in an xml, where one key is used to find firstname and second key is used to match lastname. My expected output is whenever, i match Employee firstname and lastname with 1 among multiple contractor firstname lastname, i need to retrieve the agency name which is present in the contractor xml and use it in Employee
Input
<listofdata>
<data>
<Type>Employee</Type>
<firstname>x</firstname>
<lastname>y</lastname>
</data>
<data>
<Type>Contractor</Type>
<firstname>x</firstname>
<lastname>y</lastname>
<agency>z</agency>
</data>
<data>
<Type>Contractor</Type>
<firstname>x</firstname>
<lastname>x</lastname>
<agency>a</agency>
</data>
<data>
<Type>Contractor</Type>
<firstname>a</firstname>
<lastname>y</lastname>
<agency>b</agency>
</data>
</listofdata>
Output
<listofdata>
<data>
<Type>Employee</Type>
<firstname>x</firstname>
<lastname>y</lastname>
<agency>z</agency>
</data>
<data>
<Type>Contractor</Type>
<firstname>x</firstname>
<lastname>y</lastname>
<agency>z</agency>
</data>
<data>
<Type>Contractor</Type>
<firstname>x</firstname>
<lastname>x</lastname>
<agency>a</agency>
</data>
<data>
<Type>Contractor</Type>
<firstname>a</firstname>
<lastname>y</lastname>
<agency>b</agency>
</data>
</listofdata>
XSLT:
<!--get the agency -->
<xsl:template name="getagency">
<xsl:variable name="match" select="key('firstname', firstname)
and key('lastname', lastname)" />
<xsl:if test="$match">
<xsl:variable name="lastnamevar" select="
[key('lastname', lastname)] [key('fistname', firstname)]/agency" />
<agency>
<xsl:value-of select="$lastnamevar" />
</agency>
</xsl:if>
</xsl:template>
I am calling a template whenever i encounter 'Employee' to get agency and my firstname and lastname are keys which look for firstname and lastname in Contractors. Problem is am able to get boolean true false at above if, but not able to get exact agency value, unable to tell the xslt that take the agency where firstname and lastname matches, if i use single key, the axes works, but not sure how to make it work for 2 keys
Upvotes: 2
Views: 1211
Reputation: 167696
I think you want a composite key, so in XSLT 3 using Saxon 9.8 (any edition) or Altova 2017 or 2018 you could directly use
<xsl:key name="contract" match="data[Type = 'Contractor']" composite="yes" use="firstname, lastname"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="data[Type = 'Employee' and key('contract', (firstname, lastname))]/node()[last()]">
<xsl:next-match/>
<xsl:copy-of select="key('contract', ../(firstname, lastname))/agency"/>
</xsl:template>
In XSLT 2 you can concatenate the two element values in the key with e.g. <xsl:key name="contract" match="data[Type = 'Contractor']" use="concat(firstname, '|', lastname)"/>
and use e.g.
<xsl:template match="data[Type = 'Employee' and key('contract', concat(firstname, '|', lastname))]/node()[last()]">
<xsl:next-match/>
<xsl:copy-of select="key('contract', concat(../firstname, '|', ../lastname))/agency"/>
</xsl:template>
and of course you have to spell out the identity transformation template.
Upvotes: 2
Reputation: 163458
As well as the solutions proposed by @MartinHonnen, it is possible to take two sets of values found by different keys, and form their intersection. In XSLT 2.0 you can do this directly with the intersect
operator:
key('firstName', 'John') intersect key('lastName', 'Smith')
XPath 1.0 has no operator for set intersection, but there is a convoluted solution based on set union:
<xsl:variable name="X" select="key('firstName', 'John')"/>
<xsl:variable name="Y" select="key('lastName', 'Smith')"/>
... select="$X[count(.|$Y) = count($Y)]"/>
Upvotes: 2