XSLT + regular expression replace - regex

I'm having a XML snippet like this:
...
<housenumber>23</housenumber>
...
<housenumber>453a</housenumber>
...
<housenumber>76-79</housenumber>
...
<housenumber>12 foo bar something 43</housenumber>
...
How can I cut these housenumbers into two parts with XSLT so that I get two variables - the first containing "everything from position 1 to the first occurence of a non-numeric character" and the second containing "everything else"?
So something like this:
...
<housenumber>23</housenumber>
<!-- v1 = 23, v2 = null -->
...
<housenumber>453a</housenumber>
<!-- v1 = 453, v2 = a -->
...
<housenumber>76-79</housenumber>
<!-- v1 = 76, v2 = -79 -->
...
<housenumber>12 foo bar something 43</housenumber>
<!-- v1 = 12, v2 = foo bar something 43 -->
...
Any hints/ideas?
Thanks.

As already pointed out in a comment, analyze-string can help, here is an example using XSLT 3.0 (as supported by Saxon 9.7 or Exselt) which makes use of the XPath 3.0 function analyze-string (https://www.w3.org/TR/xpath-functions-30/#func-analyze-string)
<xsl:stylesheet 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"
exclude-result-prefixes="xs math fn"
version="3.0">
<xsl:template match="root">
<xsl:variable name="matches" select="housenumber/analyze-string(., '(^[0-9]+)([^0-9]?.*)')//fn:match"/>
<xsl:variable name="v1" select="$matches//fn:group[#nr = 1]/xs:integer(.)"/>
<xsl:variable name="v2" select="$matches//fn:group[#nr = 2]/string()"/>
<integers>
<xsl:value-of select="$v1" separator=","/>
</integers>
<strings>
<xsl:value-of select="$v2" separator=","/>
</strings>
</xsl:template>
</xsl:stylesheet>
With the sample
<root>
<housenumber>23</housenumber>
...
<housenumber>453a</housenumber>
...
<housenumber>76-79</housenumber>
...
<housenumber>12 foo bar something 43</housenumber>
</root>
I get the output
<integers>23,453,76,12</integers><strings>,a,-79, foo bar something 43</strings>
With XSLT 2.0 you could use the xsl:analyze-string instruction:
<?xml version="1.0" encoding="UTF-8"?>
<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:template match="root">
<xsl:variable name="matches" as="element(match)*">
<xsl:apply-templates select="housenumber"/>
</xsl:variable>
<xsl:variable name="v1" select="$matches//group[#nr = 1]/xs:integer(.)"/>
<xsl:variable name="v2" select="$matches//group[#nr = 2]/string()"/>
<integers>
<xsl:value-of select="$v1" separator=","/>
</integers>
<strings>
<xsl:value-of select="$v2" separator=","/>
</strings>
</xsl:template>
<xsl:template match="housenumber">
<xsl:analyze-string select="." regex="(^[0-9]+)([^0-9]?.*)">
<xsl:matching-substring>
<match>
<group nr="1">
<xsl:value-of select="regex-group(1)"/>
</group>
<group nr="2">
<xsl:value-of select="regex-group(2)"/>
</group>
</match>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>

Related

XSLT 1.0 to retrieve or parse a specific value

I have an issue with the XSLT below. I need help to fix my transformation. I am using XSLT 1.0. Input can be 120KVA or 120MVA or 120.0KVA or 120.0KV. Output i want to parse into 3 parts. i.e.
<tns:ratedApparentPower>
<tns:unitSymbolUnit>VA</tns:unitSymbolUnit>
<tns:multiplier>K</tns:multiplier>
<tns:floatValue>120.0</tns:floatValue>
</tns:ratedApparentPower>
My current Transformation is:
<tns:ratedApparentPower>
<tns:unitSymbolUnit>VA</tns:unitSymbolUnit>
<tns:multiplier>
<xsl:value-of select="substring(translate(//ns0:ATTRIBUTE_GROUPS_ITEM[ns0:NAME='DISTXFR']/ns0:ATTRIBUTES/ns0:ATTRIBUTES_ITEM[ns0:NAME='KVA']/ns0:VALUE,'1234567890', ''),1,1)" />
</tns:multiplier>
<tns:floatValue>
<xsl:value-of select="substring-before(//ns0:ATTRIBUTE_GROUPS_ITEM[ns0:NAME='DISTXFR']/ns0:ATTRIBUTES/ns0:ATTRIBUTES_ITEM[ns0:NAME='KVA']/ns0:VALUE,substring(translate(//ns0:ATTRIBUTE_GROUPS_ITEM[ns0:NAME='DISTXFR']/ns0:ATTRIBUTES/ns0:ATTRIBUTES_ITEM[ns0:NAME='KVA']/ns0:VALUE,'1234567890', ''),1,1))" />
</tns:floatValue>
</tns:ratedApparentPower>
</xsl:if>
Generated O/P:
<tns:ratedApparentPower>
<tns:unitSymbolUnit>VA</tns:unitSymbolUnit>
<tns:multiplier>.</tns:multiplier>
<tns:floatValue>120</tns:floatValue>
</tns:ratedApparentPower>
My doubts are:
How to get <tns:unitSymbolUnit>VA</tns:unitSymbolUnit>? Currently, I am hardcoding it. But it can be V or VA or any other value
How to get multiplier? With my current logic I get as <tns:multiplier>.</tns:multiplier> when I have 120.0 it works fine.
With my current logic I get <tns:floatValue>120</tns:floatValue> instead of 120.0.
Is there any way I can shorten the path (//ns0:ATTRIBUTE_GROUPS_ITEM[ns0:NAME='DISTXFR']/ns0:ATTRIBUTES/ns0:ATTRIBUTES_ITEM[ns0:NAME='KVA']/ns0:VALUE) by assigning it to some variable instead of using whole path every time?
Michael I edited your template belwo to match my requiremnts and usinga callTemplate but receive empty response
<xsl:template name="convertFloatValues">
<xsl:param name="floatValue1"/>
<xsl:variable name="unit" select="translate($floatValue1, '0123456789.', '')"/>
<unitSymbolUnit>
<xsl:value-of select="substring($unit, 2)"/>
</unitSymbolUnit>
<multiplier>
<xsl:value-of select="substring($unit, 1 , 1)"/>
</multiplier>
<floatValue>
<xsl:value-of select="substring-before(., $unit)"/>
</floatValue>
</xsl:template>
Call Template example . Not sure what I am missing. Can you please help me.
<tns:ratedApparentPower>
<xsl:call-template name="convertFloatValues">
<xsl:with-param name="floatValue1" select="/ns0:OutputParameters/ns0:XXJEAM_ASSET_SEARCH_PKG-24GETAS/ns0:ATTRIBUTE_GROUPS/ns0:ATTRIBUTE_GROUPS_ITEM[ns0:NAME='DISTXFR']/ns0:ATTRIBUTES/ns0:ATTRIBUTES_ITEM[ns0:NAME='KVA']/ns0:VALUE"/>
</xsl:call-template>
</tns:ratedApparentPower>
Input:
<OutputParameters xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="*****">
<XXJEAM_ASSET_SEARCH_PKG-24GETAS>
<ATTRIBUTE_GROUPS_ITEM>
<NAME>DISTXFR</NAME>
<ATTRIBUTES>
<ATTRIBUTES_ITEM>
<COLUMN_NAME>C_ATTRIBUTE3</COLUMN_NAME>
<NAME>Volt</NAME>
<VALUE>500</VALUE>
</ATTRIBUTES_ITEM>
<ATTRIBUTES_ITEM>
<COLUMN_NAME>C_ATTRIBUTE4</COLUMN_NAME>
<NAME>KVA</NAME>
<VALUE>500.0KVA</VALUE>
</ATTRIBUTES_ITEM>
</ATTRIBUTES>
</ATTRIBUTE_GROUPS_ITEM>
</XXJEAM_ASSET_SEARCH_PKG-24GETAS>
</OutputParameters>
Consider the following example:
XML
<input>
<item>20KVA</item>
<item>120MVA</item>
<item>120.0KVA</item>
<item>120.0KV</item>
</input>
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:template match="/input">
<output>
<xsl:for-each select="item">
<xsl:variable name="unit" select="translate(., '0123456789.', '')" />
<ratedApparentPower>
<unitSymbolUnit>
<xsl:value-of select="substring($unit, 2)" />
</unitSymbolUnit>
<multiplier>
<xsl:value-of select="substring($unit, 1 , 1)" />
</multiplier>
<floatValue>
<xsl:value-of select="substring-before(., $unit)" />
</floatValue>
</ratedApparentPower>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<ratedApparentPower>
<unitSymbolUnit>VA</unitSymbolUnit>
<multiplier>K</multiplier>
<floatValue>20</floatValue>
</ratedApparentPower>
<ratedApparentPower>
<unitSymbolUnit>VA</unitSymbolUnit>
<multiplier>M</multiplier>
<floatValue>120</floatValue>
</ratedApparentPower>
<ratedApparentPower>
<unitSymbolUnit>VA</unitSymbolUnit>
<multiplier>K</multiplier>
<floatValue>120.0</floatValue>
</ratedApparentPower>
<ratedApparentPower>
<unitSymbolUnit>V</unitSymbolUnit>
<multiplier>K</multiplier>
<floatValue>120.0</floatValue>
</ratedApparentPower>
</output>

XML to Fixed Length File Using XSLT (Complex Position Logic)

We have a requirement in which need to convert XML into Fixed Length File.
First record is as header and after that we have actual records..From 2 record onwards we need to apply the logic which is mentioned below:
1.After length 45, consider 10 numbers 0000001000, what ever be the
last digit we need to check and replace by following the below
table:
"For Positive Amount: (0000001000) - (000000100{)
{= 0
A = 1
B = 2
c = 3
D = 4
E = 5
F = 6
G = 7
H = 8
I = 9
I have not that much idea so created the small XSLT , request anyone please help on the same.
Input:
<?xml version='1.0' encoding='utf-8'?>
<ZR>
<INPUT>
<I_FIL>ERES</I_FIL>
</INPUT>
<TABLES>
<T_ER>
<item>
<DATA> HEADER1111111122222222333333344456</DATA>
</item>
<item>
<DATA>778944 D4E2 EA 1234567891 2018-11-060000001000EA
0000000000000100001020D04YA30TRE0000000XXXYYY 300{ P 2018-11-05</DATA>
</item>
<item>
<DATA>987654 D4E2 EA 1987654321 2018-11-060000002001EA
0000000000000100001020D04YA30UUU0000000XXXLRB 100{ P 2018-11-05</DATA>
</item>
.
.
.
.
.
.
.
.
</T_ER>
</TABLES>
</ZR>
XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://www.w3.org/2005/xpath-functions" >
<xsl:output omit-xml-declaration="yes"/>
<xsl:param name="break" select="'
'" />
<xsl:template match="/">
<xsl:value-of select="ZR/TABLES/T_ER/item[1]/DATA"/>
<xsl:value-of select="$break" />
</xsl:template>
</xsl:stylesheet>
Expected Output:
HEADER1111111122222222333333344456
778944 D4E2 EA 1234567891 2018-11-06000000100{EA
0000000000000100001020D04YA30TRE0000000XXXYYY 300{ P 2018-11-05
987654 D4E2 EA 1987654321 2018-11-06000000200AEA
0000000000000100001020D04YA30UUU0000000XXXLRB 100{ P 2018-11-05
.
.
.
.
It looks like you just want to replace the 55th character based on your map, so you could do this...
<xsl:template match="/">
<xsl:value-of select="ZR/TABLES/T_ER/item[1]/DATA"/>
<xsl:value-of select="$break" />
<xsl:for-each select="ZR/TABLES/T_ER/item[position() > 1]/DATA">
<xsl:variable name="char" select="substring('{ABCDEFGHI', number(substring(., 55, 1)) + 1, 1)" />
<xsl:value-of select="concat(substring(., 1, 54), $char, substring(., 56))" />
<xsl:value-of select="$break" />
</xsl:for-each>
</xsl:template>
This would work in XSLT 1.0.
An XSLT 2.0 solution could be this...
<xsl:template match="/">
<xsl:value-of select="ZR/TABLES/T_ER/item[1]/DATA,
ZR/TABLES/T_ER/item[position() > 1]/DATA/concat(substring(., 1, 54), substring('{ABCDEFGHI', number(substring(., 55, 1)) + 1, 1), substring(., 56))"
separator="
" />
</xsl:template>
In XSLT 3.0, you could potentially make use of a map with has the advantage of easily being extended if you wanted to consider two or more characters instead:
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:fn="http://www.w3.org/2005/xpath-functions" >
<xsl:output omit-xml-declaration="yes"/>
<xsl:param name="break" select="'
'" />
<xsl:variable name="chars" as="map(xs:string, xs:string)">
<xsl:map>
<xsl:map-entry key="'0'" select="'{'"/>
<xsl:map-entry key="'1'" select="'A'"/>
<xsl:map-entry key="'2'" select="'B'"/>
<xsl:map-entry key="'3'" select="'C'"/>
<xsl:map-entry key="'4'" select="'D'"/>
<xsl:map-entry key="'5'" select="'E'"/>
<xsl:map-entry key="'6'" select="'F'"/>
<xsl:map-entry key="'7'" select="'G'"/>
<xsl:map-entry key="'8'" select="'H'"/>
<xsl:map-entry key="'9'" select="'I'"/>
</xsl:map>
</xsl:variable>
<xsl:template match="/">
<xsl:value-of select="ZR/TABLES/T_ER/item[1]/DATA,
ZR/TABLES/T_ER/item[position() > 1]/DATA/concat(substring(., 1, 54), $chars(substring(., 55, 1)), substring(., 56))" separator="
" />
</xsl:template>
</xsl:stylesheet>
There's probably a much nicer way in XSLT 3.0, so hopefully Martin Honnen will be along soon to say....
Well, using functions string-length, substring and translate for your specifications it can be achieved as:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output omit-xml-declaration="yes" />
<xsl:param name="break" select="'
'" />
<xsl:template match="/">
<xsl:value-of select="ZR/TABLES/T_ER/item[1]/DATA" />
<xsl:value-of select="$break" />
<xsl:for-each select="ZR/TABLES/T_ER/item[position() != 1]">
<xsl:variable name="length" select="string-length(substring(DATA,0,46))" />
<xsl:variable name="tenNumbers" select="substring(DATA, ($length + 1), 10)"/>
<xsl:variable name="charToReplace" select="translate(substring($tenNumbers, string-length($tenNumbers), 1),'0123456789','{ABCDEFGHI')" />
<xsl:value-of select="concat(substring(DATA,0,46), substring(DATA, ($length + 1), 9), $charToReplace, substring(DATA,($length+11),(string-length(DATA) + 1)))"/>
<xsl:value-of select="$break" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Compare data of 2 xmls in xslt

I am new to XSL. Hence please help me with the below.
I have 2 xmls. I have to do the following in XSL transformation.
if Employee/EmployeeInfo/FirstName = EmployeeSegment/EmployeeSummary/GivenName and Employee/EmployeeInfo/LastName = EmployeeSegment/EmployeeSummary/Surname
employeeId = EmployeeSegment/EmployeeSummary/EmpId
XML1
<Employee>
<EmployeeInfo>
<FirstName>ABC</FirstName>
<LastName>DEF</LastName>
</EmployeeInfo>
</Employee>
XML2
<EmployeeSegment>
<EmployeeSummary>
<EmpId>1234</EmpId>
<GivenName>ABC</GivenName>
<Surname>DEF</Surname>
</EmployeeSummary>
</EmployeeSegment>
I have tried the following. It is not working.
<xsl:param name="cjEmployeeSegment" select="document('CJ_Response.xml')"/>
<xsl:for-each select="/ns3:Employee/ns3:EmployeeInfo">
<xsl:variable name="empFirstName">
<xsl:value-of select="ns1:FirstName"/>
</xsl:variable>
<xsl:variable name="empLastName">
<xsl:value-of select="ns1:LastName"/>
</xsl:variable>
<xsl:for-each select="$cjEmployeeSegment/v32:EmployeeSegment/v31:EmployeeSummary">
<xsl:if test="$empFirstName=v31:GivenName and $empLastName=v31:Surname">
<ns12:EmployeeIdentifier>
<ns12:EmployeeID>
<xsl:value-of select="v31:EmpId"/>
</ns12:EmployeeID>
</ns12:EmployeeIdentifier>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
Assuming you are processing the following input:
XML
<Employee>
<EmployeeInfo>
<FirstName>ABC</FirstName>
<LastName>DEF</LastName>
</EmployeeInfo>
</Employee>
and there is another XML document named CJ_Response.xml:
<EmployeeSegment>
<EmployeeSummary>
<EmpId>1234</EmpId>
<GivenName>ABC</GivenName>
<Surname>DEF</Surname>
</EmployeeSummary>
</EmployeeSegment>
you can use the following stylesheet:
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:param name="cj_Response" select="document('CJ_Response.xml')"/>
<xsl:template match="/Employee">
<root>
<xsl:for-each select="EmployeeInfo">
<xsl:variable name="lookup" select="$cj_Response/EmployeeSegment/EmployeeSummary[GivenName = current()/FirstName and Surname = current()/LastName]" />
<xsl:if test="$lookup">
<EmployeeIdentifier>
<EmployeeID>
<xsl:value-of select="$lookup/EmpId"/>
</EmployeeID>
</EmployeeIdentifier>
</xsl:if>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
to return:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<EmployeeIdentifier>
<EmployeeID>1234</EmployeeID>
</EmployeeIdentifier>
</root>
Of course, this will fail miserably if there are two or more employees with the same name.

call template twice with similar parameters

right now I have an xml file like this:
Basically all tags appear twice, but with prefixes sideA or sideB
<root>
<sideA_foo>abc</sideA_foo>
<sideA_bar>123</sideA_bar>
<sideA_foobar>xyz</sideA_foobar>
<!--many more sideA... tags -->
<sideB_foo>def</sideB_foo>
<sideB_bar>456</sideB_bar>
<sideB_foobar>uvw</sideB_foobar>
<!--many more sideB... tags -->
</root>
then I have a template like this
<xsl:template name="template1">
<xsl:param name = "foo"/>
<xsl:param name = "bar"/>
<xsl:param name = "foobar"/>
<!-- many more params -->
<!-- do anything here -->
</xsl:template>
Is there an elegant way to call this template twice with all of its params,
<xsl:with-param name = "foo" select = "sideA_foo"/> etc.
<xsl:with-param name = "foo" select = "sideB_foo"/> etc.
without wirting all of this very verbosely, which I hate?
Here's one way you could consider:
Given an example input:
<root>
<sideA_width>5</sideA_width>
<sideA_length>7</sideA_length>
<sideB_width>6</sideB_width>
<sideB_length>3</sideB_length>
</root>
the following stylesheet:
<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:template match="/root">
<output>
<xsl:call-template name="side-area">
<xsl:with-param name="side" select="'sideA'"/>
</xsl:call-template>
<xsl:call-template name="side-area">
<xsl:with-param name="side" select="'sideB'"/>
</xsl:call-template>
</output>
</xsl:template>
<xsl:template name="side-area">
<xsl:param name="side"/>
<xsl:param name="width" select="*[name()=concat($side, '_width')]"/>
<xsl:param name="length" select="*[name()=concat($side, '_length')]"/>
<xsl:element name="{$side}_area">
<xsl:value-of select="$width * $length"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
will return:
<?xml version="1.0" encoding="UTF-8"?>
<output>
<sideA_area>35</sideA_area>
<sideB_area>18</sideB_area>
</output>
Note, however, that explicit naming of elements is more efficient - sometimes much more efficient. The really elegant solution would be to normalize your input before it gets to you, so that it looks more like (for example):
<root>
<rect id="X">
<width>5</width>
<length>7</length>
</rect>
<rect id="Y">
<width>6</width>
<length>3</length>
</rect>
</root>

XPATH for first element whose name is among the names of some other elements

<choices>
<sic />
<corr />
<reg />
<orig />
</choices>
<choice>
<corr>Red</corr>
<sic>Blue</sic>
<choice>
I want to select the first element in <choice> whose name matches the name of any element in <choices>.
If name(node-set) returned a list of names instead of only the name of the first node, I could use
select="choice/*[name() = name(choices/*)][1]"
But it doesn't (at least not in 1.0), so instead I join the names together in a string and use contains():
<xsl:variable name="choices.str">
<xsl:for-each select="choices/*">
<xsl:text> </xsl:text><xsl:value-of select="concat(name(),' ')"/>
</xsl:for-each>
</xsl:variable>
<xsl:apply-templates select="choice/*[contains($choices.str,name())][1]"/>
and get what I want:
Red, the value of <corr>
Is there a more straightforward way?
I. Use this XPath 2.0 one-liner:
/*/choice/*[name() = /*/choices/*/name()][1]
When this XPath expression is evaluated against the following XML document (the provided one, but corrected to become a well-formed XML document):
<t>
<choices>
<sic />
<corr />
<reg />
<orig />
</choices>
<choice>
<corr>Red</corr>
<sic>Blue</sic>
</choice>
</t>
the correct element is selected:
<corr>Red</corr>
II. XSLT 1.0 (no keys!):
<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:variable name="vNames">
<xsl:for-each select="/*/choices/*">
<xsl:value-of select="concat(' ', name(), ' ')"/>
</xsl:for-each>
</xsl:variable>
<xsl:template match="/">
<xsl:copy-of select=
"/*/choice/*
[contains($vNames, concat(' ', name(), ' '))]
[1]"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the same XML document (above), again the correct element is selected (and copied to the output):
<corr>Red</corr>
III. Using keys:
<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="kChoiceByName" match="choice/*"
use="boolean(/*/choices/*[name()=name(current())])"/>
<xsl:template match="/">
<xsl:copy-of select="/*/choice/*[key('kChoiceByName', true())][1]"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied against the same XML document (above), the same correct result is produced:
<corr>Red</corr>
It is recommended to the reader to try to understand how this all "works" :)
You can use the key() function like this...
When this input document...
<t>
<choices>
<sic />
<corr />
<reg />
<orig />
</choices>
<choice>
<corr>Red</corr>
<sic>Blue</sic>
</choice>
</t>
...is supplied as input to this XSLT 1.0 style-sheet...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kChoices" match="choices/*" use="name()" />
<xsl:template match="/">
<xsl:variable name="first-choice" select="(*/choice/*[key('kChoices',name())])[1]" />
<xsl:value-of select="$first-choice" />
<xsl:text>, the value of <</xsl:text>
<xsl:value-of select="name( $first-choice)" />
<xsl:text>></xsl:text>
</xsl:template>
</xsl:stylesheet>
...this output text is produced...
Red, the value of <corr>
XSLT 2.0 Aside
In XSLT 2.0, you would be able to use the following alternatives for the computation of the $first-choice variable...
Option 1:
(*/choice/*[for $c in . return ../../choices/*[name()=name($c)]])[1]
Option 2:
(*/choice/*[some $c in ../../choices/* satisfies name($c)=name()])[1]