Process an element both through a generic and a targeted template - templates

I'm new at XSL and still not sure of some of my terminology. I'm currently facing something I can't seem to crack. I'm attempting to
Search through all data nodes (leaf elements?) of input XML and replace text
Search through all attribute values in input XML and replace text
Copy over other nodes to output
Copy over processor instructions and comments to output
Match and process specific nodes in the input
The issues I'm facing are:
A. Not sure of the terminology (see the comments in files below) and the tack I'm taking in attacking this
B. The template (5) above, whenever it matches a node, seems to be preventing the other templates (1 and 2) from processing it
If it makes a difference, I'm running this on Windows, using Microsoft's processor and am using XSLT 1.0. I've included simplified versions of the input (Input.xml), XSLT (Transform.xslt) and the output (Output.xml) I'm getting.
I did try using "mode" to run the targeted template (5) from the generic search and replace templates (1 and 2) but, in that case, template 1 and 2 run but the targeted template (5) itself doesn't run.
I'd appreciate any comments and suggestions.
Input.xml
<?xml version="1.0" encoding="UTF-8"?>
<List>
<Item Name="Item1" Text="abcd"/>
<Item Name="Itembc" Text="qrst"/>
<Item Name="Special" Text="Hello, Worldbc"/>
<Item Name="Special" Text=""/>
</List>
Transform.xslt
<?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" xmlns:myjs="urn:custom-javascript" exclude-result-prefixes="msxsl myjs">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<msxsl:script language="JavaScript" implements-prefix="myjs">
<![CDATA[
function EscapeRegExp(str)
{
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}
function StringReplace(strWhere, strWhat, strBy, strFlags)
{
return strWhere.replace ( new RegExp(EscapeRegExp(strWhat), strFlags), strBy);
}
]]>
</msxsl:script>
<!-- ********************************************************************************************************************** -->
<!-- -->
<!-- Because of the following 4 templates, the identity transform is not needed in this XSLT -->
<!-- -->
<!-- ********************************************************************************************************************** -->
<!-- 1 of 4: Copy all nodes from source XML to the final XML, searching and replacing -->
<!-- Modify (1 of 4) and (2 of 4) to support additional replacement -->
<!-- Search and Replace in attributes -->
<xsl:template match="#*">
<xsl:attribute name="{name()}" namespace="{namespace-uri()}">
<xsl:variable name="TempAttrValue" select="."/>
<xsl:value-of select="myjs:StringReplace(string($TempAttrValue), 'bc', '2', 'g')"/>
</xsl:attribute>
<!-- xsl:apply-templates mode="TargetedTemplate"/ -->
</xsl:template>
<!-- 2 of 4: Copy all nodes from source XML to the final XML, searching and replacing -->
<!-- Modify (1 of 4) and (2 of 4) to support additional replacement -->
<!-- Search and Replace in data nodes -->
<xsl:template match="text()">
<xsl:variable name="TempTextValue" select="."/>
<xsl:value-of select="myjs:StringReplace(string($TempTextValue), 'bc', '3', 'g')"/>
<!-- xsl:apply-templates mode="TargetedTemplate"/ -->
</xsl:template>
<!-- 3 of 4: Copy all nodes from source XML to the final XML, searching and replacing -->
<!-- Process element nodes but not attributes -->
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- 4 of 4: Copy all nodes from source XML to the final XML, searching and replacing -->
<!-- Leave the comment nodes and processing instruction nodes alone -->
<xsl:template match="comment() | processing-instruction()">
<xsl:copy/>
</xsl:template>
<!-- 5 : Process specific nodes -->
<!-- Assumes an item in the input called Special and fills it with (No Data) if it is empty -->
<!-- Seems to be interfering with 1-4 above. Changing the [#Name='Special'] to [#Name='SpecialA'] will let 1-4 above to work -->
<xsl:template match="Item[#Name='Special']/#Text">
<xsl:attribute name="Text">
<xsl:variable name="TempSpecialText" select="."/>
<xsl:choose>
<xsl:when test="($TempSpecialText = '')">(No Data)</xsl:when>
<xsl:otherwise><xsl:value-of select="$TempSpecialText"/></xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
Output.xml
<?xml version="1.0" encoding="UTF-8"?>
<List>
<Item Name="Item1" Text="a2d">
</Item>
<Item Name="Item2" Text="qrst">
</Item>
<Item Name="Special" Text="Hello, Worldbc">
</Item>
<Item Name="Special" Text="(No Data)">
</Item>
</List>
Output - Desired.xml
<?xml version="1.0" encoding="UTF-8"?>
<List>
<Item Name="Item1" Text="a2d">
</Item>
<Item Name="Item2" Text="qrst">
</Item>
<Item Name="Special" Text="Hello, World2">
</Item>
<Item Name="Special" Text="(No Data)">
</Item>
</List>

I think the main problem is with your final template
<xsl:template match="Item[#Name='Special']/#Text">
<xsl:attribute name="Text">
<xsl:variable name="TempSpecialText" select="."/>
<xsl:choose>
<xsl:when test="($TempSpecialText = '')">(No Data)</xsl:when>
<xsl:otherwise><xsl:value-of select="$TempSpecialText"/></xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:template>
This will match your <Item Name="Special" Text="Hello, Worldbc"> element ahead of the template that just matches #*. However, the Text attribute is not empty, and your xsl:otherwise simply outputs the value again, and doesn't do the replace you want. The <xsl:apply-templates/> here is unnecessary because attributes do not have any child nodes to select.
What you can do is this...
<xsl:template match="Item[#Name='Special']/#Text">
<xsl:attribute name="Text">
<xsl:variable name="TempSpecialText" select="."/>
<xsl:choose>
<xsl:when test="($TempSpecialText = '')">(No Data)</xsl:when>
<xsl:otherwise>
<xsl:value-of select="myjs:StringReplace(string($TempSpecialText), 'bc', '2', 'g')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:template>
But this is duplication of code. A better solution would be to change the template only match empty attributes, like so:
<xsl:template match="Item[#Name='Special']/#Text[. = '']">
<xsl:attribute name="Text">(No Data)</xsl:attribute>
</xsl:template>
That way it will only match the <Item Name="Special" Text=""/> element, where as the <Item Name="Special" Text="Hello, Worldbc"/> will be matched by the generic #* template.
Be wary of using Microsoft specific extension functions, as this obviously limits you to running on Microsoft platforms. If you limited to XSLT 1.0, this means using a recursive template. (See Find and replace entity in xslt as an example). Alternatively, if you can switch to XSLT 2.0, the replace function comes as standard.

Would this work for you?
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:myjs="urn:custom-javascript"
exclude-result-prefixes="msxsl myjs">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<msxsl:script language="JavaScript" implements-prefix="myjs">
<![CDATA[
function EscapeRegExp(str)
{
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}
function StringReplace(strWhere, strWhat, strBy, strFlags)
{
return strWhere.replace ( new RegExp(EscapeRegExp(strWhat), strFlags), strBy);
}
]]>
</msxsl:script>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*[contains(., 'bc')]">
<xsl:attribute name="{name()}" namespace="{namespace-uri()}">
<xsl:value-of select="myjs:StringReplace(string(.), 'bc', '2', 'g')"/>
</xsl:attribute>
</xsl:template>
<xsl:template match="#*[not(string())]">
<xsl:attribute name="{name()}" namespace="{namespace-uri()}">
<xsl:text>(No Data)</xsl:text>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>

Related

XSLT: Copying node data to another node by matching attribute value, Efficiently?

I coded the XSLT to copy one node data to another by validating the attribute value, I got the desired output but I'm curious to know whether there is an efficient way to do this or if this is the only way to do it. [I'm not an XSLT expert] Can someone help !!!
Please use this link to check instantly.
https://xsltfiddle.liberty-development.net/pNvtBH2/3
Actual XML:
<?xml version="1.0" encoding="utf-8" ?>
<section>
<p>note 1 : 1</p>
<p>note 2 : 2</p>
<p>note 3 : 3</p>
<note id="test1">hello one</note>
<note id="test2">hello two</note>
<note id="test3">hello <i>three</i></note>
<note id="test4">hello <i>four</i></note>
</section>
Output:
<?xml version="1.0" encoding="UTF-8"?><section>
<p>note 1 : <a>hello one</a></p>
<p>note 2 : <a>hello two</a></p>
<p>note 3 : <a>hello <i>three</i></a></p>
<note id="test4">hello <i>four</i></note>
</section>
XSLT Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="xml" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="a">
<a>
<xsl:variable name="href" select="#href" />
<xsl:choose>
<xsl:when test="$href = //note/#id">
<xsl:copy-of select="//note[#id=$href]/node()" />
</xsl:when>
</xsl:choose>
</a>
</xsl:template>
<xsl:template match="note">
<xsl:choose>
<xsl:when test="#id = //a/#href">
<xsl:apply-templates select="node" />
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Whether it's efficient or not depends on how smart the optimizer in your XSLT processor is. Saxon-EE will do a pretty good job on this, Saxon-HE less so.
If you want to make it efficient on all processors, use keys. Replace an expression like //note[#id=$href] with a call on the key() function. Declare the key as
<xsl:key name="k" match="note" use="id"/>
and then you can get the matching nodes using key('k', $href).
The xsl:when test="$href = //note/#id" is redundant, as xsl:copy-of will do nothing if nothing is selected.
In the note template, I'm not sure what
<xsl:when test="#id = //a/#href">
<xsl:apply-templates select="node" />
</xsl:when>
is trying to achieve, because you don't have any elements named "node". If the aim is to avoid processing a note element at this stage if it was already processed from an a element, then you could build another index with
<xsl:key name="a" match="a" use="1"/>
and replace test="#id = //a/#href" with test="key('a', #href)"

How do I generate a XSLT stylesheet for the following TEI snipet?

i wanted to ask you for help. I am totally new to XSLT and i wanted to know if someone can show me the right XSLT stylesheet for the following TEI-snipet:
<div>
<head>Weitere Aufzählungen</head>
<list rend="numbered">
<item n="1">A</item>
<item n="2">B</item>
<item n="3">C<list rend="numbered">
<item n="3.1">a</item>
<item n="3.2">b</item>
</list>
</item>
</list>
</div>
The Output should look like that in the HTML-Document:
1. A
2. B
3. C
3.1 a
3.2 b
Thank you so much for helping me :)
If you want a text output, the following stylesheet with <xsl:output method="text" /> will do. It distinguishes the level of indentation by counting the item ancestor nodes and adds an extra . on level 0.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="yes" />
<xsl:variable name="newLine" select="'
'" />
<xsl:template match="text()" />
<xsl:template match="/div/list">
<xsl:apply-templates select="item" />
</xsl:template>
<xsl:template match="item">
<xsl:for-each select="ancestor::item"><xsl:text> </xsl:text></xsl:for-each>
<xsl:value-of select="#n" />
<xsl:if test="not(ancestor::item)"><xsl:text>.</xsl:text></xsl:if>
<xsl:value-of select="concat(' ',text(),$newLine)" />
<xsl:apply-templates select="list" />
</xsl:template>
</xsl:stylesheet>
Output is:
1. A
2. B
3. C
3.1 a
3.2 b
BTW you may need to add an appropriate namespace declaration for the TEI namespace to the xsl:stylesheet element.

How to count created node XSL?

I was asking myself a question.
I'm on project where I've to transform a .XML file into an other one (after a treatment) but I've to do a numbered list. I know the count(//node) function but I don't think we can count the created nodes with.
For example this is what my .xsl looks like :
<xsl:template match="/">
<Type>
<List>
<xsl:apply-templates mode="PartOne" select="/Stuff/Info/TypeA"/>
<xsl:apply-templates mode="PartOneBis" select="/Stuff/Info/TypeB"/>
</List>
<AnotherList>
<xsl:apply-templates mode="PartTwo" select="/Stuff/Info/TypeB"/>
</AnotherList>
</Type>
</xsl:template>
<xsl:template mode="PartOne" match="/Stuff/Info/TypeA">
<PartOne indexlist="{position()-1}">
... treatment ...
</PartOne>
</xsl:template>
<xsl:template mode="PartOneBis" match="/Stuff/Info/TypeB">
<xsl:if test="TypeB_Indice != 'stuff'">
<PartOne indexlist="{count(//TypeA) + position()-1}">
... treatment ...
</PartOne>
</xsl:if>
</xsl:template>
<xsl:template mode="PartTwo" match="/Stuff/Info/TypeB">
<xsl:if test="TypeB_Indice = 'stuff'">
<PartTwo indexlist="{count(//TypeA) + position()-1}">
... treatment ...
</PartTwo>
</xsl:if>
</xsl:template>
And this is what my .xml looks like:
<Stuff>
<Info>
<TypeA>
<TypeA_Stuff/>
<TypeA_Indice>xxx</TypeA_Indice>
</TypeA>
<TypeB>
<TypeB_Stuff/>
<TypeB_Indice>xxx</TypeB_Indice>
</TypeB>
</Info>
</Stuff>
---------------------- edit ----------------------------
The conditions for the PartOneBis is more complacated than the one I put in this code, there's more like 6 differents factor that can change its state from ok to not ok.
I was thinking of a for-each with an if and an incrementation but this don't work because you can't overwrite a variable or may be I'm wrong in my method.
If there's a way to count the node create before your point without having to create two .xml or use a c++ function I'll like to know it.
Thanks.
I need to put the "partOne" type first and the "partTwo" second but in the xml I've got, there's some conditions that makes that the TypeB is a partOne otherwise the other cases'll be a partTwo.
TypeA -> partOne
TypeB -> if (something) partOne else partTwo
But the condition depend of several value which don't came from the same node but the wanted result is something like that
<PartOne indexlist="0">
SomeStuff
</PartOne>
<PartOne indexlist="1">
SomeStuff
</PartOne>
<PartTwo indexlist="2">
SomeStuff
</PartTwo>
I find it impossible to follow your example. Consider a simpler one:
XML
<input>
<item>red</item>
<item>red red</item>
<item>red blue</item>
<item>red green</item>
<item>blue</item>
<item>blue blue</item>
<item>blue green</item>
<item>blue red</item>
<item>green</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:strip-space elements="*"/>
<xsl:template match="/input">
<xsl:variable name="red-items" select="item[contains(., 'red')]" />
<xsl:variable name="blue-items" select="item[contains(., 'blue')]" />
<output>
<red-list>
<xsl:apply-templates select="$red-items"/>
</red-list>
<blue-list>
<xsl:apply-templates select="$blue-items">
<xsl:with-param name="n" select="count($red-items)"/>
</xsl:apply-templates>
</blue-list>
</output>
</xsl:template>
<xsl:template match="item">
<xsl:param name="n" select="0"/>
<item i="{$n + position()}">
<xsl:value-of select="." />
</item>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<red-list>
<item i="1">red</item>
<item i="2">red red</item>
<item i="3">red blue</item>
<item i="4">red green</item>
<item i="5">blue red</item>
</red-list>
<blue-list>
<item i="6">red blue</item>
<item i="7">blue</item>
<item i="8">blue blue</item>
<item i="9">blue green</item>
<item i="10">blue red</item>
</blue-list>
</output>

Getting value in between keys using XSLT

I Need to get the value in the XML file in between each key. For example, I have a list of keys to be used, and each key there's a corresponding output element. The keys can be placed anywhere, there is no proper order in where the key is needed to place. I need to do this in XSLT 2.0, and I don't have any idea on how will I do this.
Keys: Element:
/OPDH/ - ROOT/ELEMENT1/ABCD
/EKPH/ - ROOT/ELEMENT2/POIU
/SGDE/ - ROOT/ELEMENT3/WXYZ
...some other keys...
NOTE: Keys: is in BOLD, and Element is in ITALIC BOLD.
If I have a sample input like this:
1.)
<DATA>/OPDH/FLOWING SOLUTION/SGDE/Number0983713/EKPH/Sample test/some other keys/</DATA>
OR it can be:
2.)
<DATA>/some other keys/afdsf/SGDE/Number0983713/some other keys/PIHSAGA/OPDH/FLOWING SOLUTION/some other keys/No exception/EKPH/Sample test/some other keys/</DATA>
The expected output should look like this:
1.
<ROOT>
<ELEMENT1>
<ABCD>FLOWING SOLUTION</ABCD>
</ELEMENT1>
<ELEMENT2>
<POIU>Sample test</POIU>
</ELEMENT2>
<ELEMENT3>
<SGDE>Number0983713</SGDE>
</ELEMENT3>
...some other keys...
</ROOT>
2.
<ROOT>
...some other keys...
<ELEMENT3>
<SGDE>Number0983713</SGDE>
</ELEMENT3>
...some other keys...
<ELEMENT1>
<ABCD>FLOWING SOLUTION</ABCD>
</ELEMENT1>
...some other keys...
<ELEMENT2>
<POIU>Sample test</POIU>
</ELEMENT2>
...some other keys...
</ROOT>
Thank you.
Here is a partial suggestion that uses analyze-string:
<?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" xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf" version="2.0">
<xsl:param name="keys">
<element key="/OPDH/">ROOT/ELEMENT1/ABCD</element>
<element key="/EKPH/">ROOT/ELEMENT2/POIU</element>
<element key="/SGDE/">ROOT/ELEMENT3/WXYZ</element>
<element key="/some other keys/">ROOT/FOO/BAR</element>
</xsl:param>
<xsl:output indent="yes"/>
<xsl:variable name="pattern" as="xs:string"
select="concat('(', string-join($keys/element/#key, '|'), ')', '(.*?)', '(', string-join($keys/element/#key, '|'), ')')"/>
<xsl:key name="ref" match="element" use="#key"/>
<xsl:function name="mf:extract" as="element()*">
<xsl:param name="input" as="xs:string"/>
<xsl:analyze-string select="$input" regex="{$pattern}">
<xsl:matching-substring>
<xsl:if test="position() eq 1">
<element path="{key('ref', regex-group(1), $keys)}">
<xsl:value-of select="regex-group(2)"/>
</element>
<xsl:sequence
select="mf:extract(substring($input, string-length(concat(regex-group(1), regex-group(2))) + 1))"
/>
</xsl:if>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:function>
<xsl:template match="DATA">
<xsl:copy>
<xsl:sequence select="mf:extract(.)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This transforms the input
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<DATA>/OPDH/FLOWING SOLUTION/SGDE/Number0983713/EKPH/Sample test/some other keys/</DATA>
<DATA>/some other keys/afdsf/SGDE/Number0983713/some other keys/PIHSAGA/OPDH/FLOWING SOLUTION/some other keys/No exception/EKPH/Sample test/some other keys/</DATA>
</Root>
into list of elements with the extracted data and the path to build:
<DATA>
<element path="ROOT/ELEMENT1/ABCD">FLOWING SOLUTION</element>
<element path="ROOT/ELEMENT3/WXYZ">Number0983713</element>
<element path="ROOT/ELEMENT2/POIU">Sample test</element>
</DATA>
<DATA>
<element path="ROOT/FOO/BAR">afdsf</element>
<element path="ROOT/ELEMENT3/WXYZ">Number0983713</element>
<element path="ROOT/FOO/BAR">PIHSAGA</element>
<element path="ROOT/ELEMENT1/ABCD">FLOWING SOLUTION</element>
<element path="ROOT/FOO/BAR">No exception</element>
<element path="ROOT/ELEMENT2/POIU">Sample test</element>
</DATA>
I am not quite sure whether that is doing the right job as I am not sure what determines the order and contents of the two samples you have provided and what e.g. /some other keys/ is meant to express. Tell us whether the result has the data you want or clarify your question and the samples you have shown. It should be easy to generate the XML from the above intermediary results once we have established that the right data is extracted.
You wrote very little about keys, so I assume that:
Your input file contains both:
key list (in KEYS tag),
actual source (in DATA tag).
Both these tags are children of the source ROOT tag.
KEYS tag contains in each row a pair of key value and output path, where
respective content for this key shoud be placed.
Assume that your full input is:
<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
<KEYS>
/OPDH/ - ROOT/ELEMENT1/ABCD
/EKPH/ - ROOT/ELEMENT2/POIU
/SGDE/ - ROOT/ELEMENT3/SGDE
</KEYS>
<DATA>/OPDH/FLOWING SOLUTION/SGDE/Number0983713/EKPH/Sample test/</DATA>
</ROOT>
Then you can write the XSLT as follows:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="ROOT">
<xsl:copy>
<!-- Divide KEYS into rows -->
<xsl:variable name="keys_1" select="tokenize(KEYS, '
')"/>
<!-- # of rows -->
<xsl:variable name="nn" select="count($keys_1)"/>
<!-- Drop 1st and last (empty) row -->
<xsl:variable name="keys" select="subsequence($keys_1, 2, $nn - 2)"/>
<!-- Divide DATA into tokens -->
<xsl:variable name="data_1" select="tokenize(DATA, '/')"/>
<!-- # of tokens -->
<xsl:variable name="nn" select="count($data_1)"/>
<!-- Drop 1st and last (empty) token -->
<xsl:variable name="data" select="subsequence($data_1, 2, $nn - 2)"/>
<!-- Generate output data for each row from keys -->
<xsl:for-each select="$keys">
<!-- Divide the keys row into tokens -->
<xsl:variable name="parts" select="tokenize(., '/')"/>
<!-- # of tokens -->
<xsl:variable name="nn" select="count($parts)"/>
<!-- Source key - token No 2 (after the 1st '/') -->
<xsl:variable name="srcKey" select="$parts[2]"/>
<!-- path - tokens after 'ROOT' -->
<xsl:variable name="path" select="subsequence($parts, 4)"/>
<!-- Open tags given in path -->
<xsl:for-each select="$path">
<xsl:text>
</xsl:text>
<!-- Spacing -->
<xsl:variable name="nn" select="position()"/>
<xsl:value-of select=
"string-join((for $i in 1 to $nn return ' '), '')"/>
<!-- Print opening tag -->
<xsl:value-of select="concat('<', ., '>')"
disable-output-escaping="yes"/>
</xsl:for-each>
<!-- Find position of the source key in data -->
<xsl:variable name="ind" select="index-of($data, $srcKey)[1]"/>
<!-- Get data from the next token -->
<xsl:value-of select="$data[$ind + 1]"/>
<!-- Close tags given in path -->
<xsl:for-each select="reverse($path)">
<xsl:variable name="nn" select="position()"/>
<!-- Spacing and NewLine - but not for the most inner tag -->
<xsl:if test="$nn > 1">
<xsl:text>
</xsl:text>
<xsl:value-of select=
"string-join((for $i in 1 to last() - $nn + 1 return ' '), '')"/>
</xsl:if>
<!-- Print closing tag -->
<xsl:value-of select="concat('</', ., '>')"
disable-output-escaping="yes"/>
</xsl:for-each>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()"/></xsl:copy>
</xsl:template>
</xsl:transform>

msxsl:node-set() not recognized

I am trying to pull nodes out of a node set stored in a variable using the msxsl:node-set() function and am not getting anything. My xml looks like this:
<Root>
<Items olditemnumber="100" newitemnumber="200">
<Item ItemNumber="100" ItemAliasCode="1001" ItemCode="X" />
<Item ItemNumber="100" ItemAliasCode="1002" ItemCode="X" />
<Item ItemNumber="200" ItemAliasCode="2001" ItemCode="X" />
<Item ItemNumber="200" ItemAliasCode="2003" ItemCode="X" />
<Item ItemNumber="100" ItemAliasCode="1003" ItemCode="P" />
<Item ItemNumber="100" ItemAliasCode="1004" ItemCode="P" />
<Item ItemNumber="200" ItemAliasCode="2002" ItemCode="P" />
</Items>
</Root>
In my xslt I try to populate a variable with a subset of the nodes and then call them using the msxsl:node-set() function. This doesn't return anything however.
XSLT looks like this:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:template match="//Root">
<xsl:variable name="OldItemNumber" select="/Items/#olditemnumber"/>
<xsl:variable name="NewItemNumber" select="/Items/#newitemnumber"/>
<xsl:variable name="OldItems">
<xsl:value-of select="//Item[#ItemNumber = $OldItemNumber]"/>
</xsl:variable>
<xsl:variable name="NewItems">
<xsl:value-of select="//Item[#ItemNumber = $NewItemNumber]"/>
</xsl:variable>
<xsl:for-each select="msxsl:node-set($OldItems)/Item">
...work
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The XSLT skips over the for-each loop, though I see in the watch that the the Xpath query grabs the right nodes in assigning the variables. The watch also tells me that the msxsl:node-set() function is undefined. Any help would be appreciated. What am I missing?
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:template match="//Root">
<xsl:variable name="OldItemNumber" select="/Items/#olditemnumber"/>
<xsl:variable name="NewItemNumber" select="/Items/#newitemnumber"/>
<xsl:variable name="OldItems" select="//Item[#ItemNumber = $OldItemNumber]"/>
<xsl:variable name="NewItems" select="//Item[#ItemNumber = $NewItemNumber]"/>
<xsl:for-each select="$OldItems">
...work
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
msxsl:node-set is for converting a result tree fragment (a.k.a. RTF) to a node set, which is not needed on your case.
xsl:value-of is for creating text nodes, so don't use it for selecting nodes of the input tree that you want to further query/process.