How to replace a template result into another section of XML - xslt

Using this XML
<!DOCTYPE ADI SYSTEM "ADI.DTD">
<ADI>
<Asset>
<Metadata>
<AMS Asset_Class="title" Asset_ID="HSHT2344862006150001" Asset_Name="HSHT2344862006150001_ALON_HD" Creation_Date="2020-07-07" Description="Alone-title" Product="SVOD" Verb="" Version_Major="1" Version_Minor="0"/>
<App_Data App="MOD" Name="Type" Value="title"/>
<App_Data App="MOD" Name="Title" Value="Alone S7 Ep02 HD"/>
<App_Data App="MOD" Name="Title_Brief" Value="Alone S7 Ep02 HD"/>
<App_Data App="MOD" Name="Category" Value="History/Alone"/>
<App_Data App="MOD" Name="Category" Value="TV Shows/History/Alone"/>
</Metadata>
</Asset>
</ADI>
I'm trying to accomplish three things in a XSLT Transformation.
Take one of the Category values in App_Data and extract the string after the last "/"
Replace Title and Title_Brief Values with the resulting string.
Remove the Category lines
I've been able to alter the Category lines down to the string that I want to substitute into the Title and Title_Brief Values using advice from this post however my result only replaces both Category lines.
This is my XSLT
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output encoding="UTF-8" doctype-system="ADI.DTD" method="xml" indent="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template name="substring-after-last">
<xsl:param name="string"/>
<xsl:param name="char"/>
<xsl:choose>
<xsl:when test="contains($string, $char)">
<xsl:call-template name="substring-after-last">
<xsl:with-param name="string" select="substring-after($string, $char)"/>
<xsl:with-param name="char" select="$char"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/ADI/Asset/Metadata/App_Data[#Name='Category']">
<xsl:call-template name="substring-after-last">
<xsl:with-param name="string" select="#Value"/>
<xsl:with-param name="char" select="'/'"/>
</xsl:call-template>
</xsl:template>
</xsl:transform>
The result being
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ADI
SYSTEM "ADI.DTD">
<ADI>
<Asset>
<Metadata>
<AMS Asset_Class="title"
Asset_ID="HSHT2344862006150001"
Asset_Name="HSHT2344862006150001_ALON_HD"
Creation_Date="2020-07-07"
Description="Alone-title"
Product="SVOD"
Verb=""
Version_Major="1"
Version_Minor="0"/>
<App_Data App="MOD" Name="Type" Value="title"/>
<App_Data App="MOD" Name="Title" Value="Alone S7 Ep02 HD"/>
<App_Data App="MOD" Name="Title_Brief" Value="Alone S7 Ep02 HD"/>
Alone
Alone
</Metadata>
</Asset>
</ADI>

xsl:attribute allowed me to accomplish my result, so the last template looks like
<xsl:template match="/ADI/Asset/Metadata/App_Data[#Name='Category']">
<App_Data App="{#App}" Name="Title">
<xsl:attribute name="Value">
<xsl:call-template name="substring-after-last">
<xsl:with-param name="string" select="#Value"/>
<xsl:with-param name="char" select="'/'"/>
</xsl:call-template>
</xsl:attribute>
</App_Data>
</xsl:template>
Also removing the original Title and Title_Brief with
<xsl:template
match="/ADI/Asset/Metadata/App_Data[#Name='Title' or #Name='Title_Brief'">
</xsl:template>

Related

XSLT to transform given query with given parms

Here is the given XML-
<INPUT>
<pSql>select * from cntwrk where moddte>= :from_date and ins_dt < :to_date</parameterizedSql>
<arguments>
<dataType>DATETIME</dataType>
<values>2019-07-24T00:00:01</values>
<key>from_date</key>
</arguments>
<arguments>
<dataType>DATETIME</dataType>
<values>2019-09-23T00:00:01</values>
<key>to_date</key>
</arguments>
</INPUT>
I need to build a xslt to have the final query has
select * from cntwrk where moddte>= (to_date('2019-07-24 00:00:01','yyyy-MM-dd HH24:mi:ss')) and
ins_dt < (to_date('2019-09-23 00:00:01','yyyy-MM-dd HH24:mi:ss'))
output.
That is replace the :from_date with arguments/values after some concatenation.
Please find the XSLT that i tried, but could not get the desired output with using variables.
<?xml version="1.0"?>
<xsl:transform exclude-result-prefixes="xsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" indent="yes" />
<xsl:variable name="q_var" select="INPUT/parameterizedSql" />
<xsl:param name="find_var" select="concat(':',INPUT/arguments/key)" />
<xsl:param name="re_var" select="INPUT/arguments/values" />
<xsl:template match="INPUT">
<xsl:apply-templates select="arguments[dataType[text() = 'DATETIME']]" />
</xsl:template>
<xsl:template match="INPUT">
<xsl:for-each select="//arguments">
<xsl:call-template name="replace-string">
<xsl:with-param name="text" select="$q_var" />
<xsl:with-param name="replace" select="$find_var" />
<xsl:with-param name="with" select="$re_var" />
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="replace-string">
<xsl:param name="text" />
<xsl:param name="replace" />
<xsl:param name="with" />
<xsl:choose>
<xsl:when test="contains($text,$replace)">
<xsl:value-of select="substring-before($text,$replace)" />
<xsl:value-of select="$with" />
<xsl:call-template name="replace-string">
<xsl:with-param name="text" select="substring-after($text,$replace)" />
<xsl:with-param name="replace" select="$replace" />
<xsl:with-param name="with" select="$with" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:transform>
The first thing to learn here is that xsl:for-each is not a loop. Each node in the selected node-set is processed individually. You cannot pass the result of one instance to another.
There are two possible ways to process the given string sequentially: one is a technique called sibling recursion, and the other is a through a named recursive template. The following example will demonstrate the 2nd method.
For simplicity, I will assume that each argument appears in the given string exactly once. If this assumption is not true, then you will need to call another recursive template to perform the actual replacement of the currently processed argument in the given string when calling the next iteration.
XML (corrected)
<INPUT>
<parameterizedSql>select * from cntwrk where moddte>= :from_date and ins_dt < :to_date</parameterizedSql>
<arguments>
<dataType>DATETIME</dataType>
<values>2019-07-24T00:00:01</values>
<key>from_date</key>
</arguments>
<arguments>
<dataType>DATETIME</dataType>
<values>2019-09-23T00:00:01</values>
<key>to_date</key>
</arguments>
</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:call-template name="insert-arguments">
<xsl:with-param name="string" select="parameterizedSql"/>
<xsl:with-param name="arguments" select="arguments"/>
</xsl:call-template>
</OUTPUT>
</xsl:template>
<xsl:template name="insert-arguments">
<xsl:param name="string"/>
<xsl:param name="arguments"/>
<xsl:choose>
<xsl:when test="$arguments">
<!-- recursive call -->
<xsl:call-template name="insert-arguments">
<xsl:with-param name="string">
<!-- insert current argument -->
<xsl:variable name="argument" select="$arguments[1]" />
<xsl:variable name="search-string" select="concat(':', $argument/key)" />
<xsl:value-of select="substring-before($string, $search-string)"/>
<xsl:value-of select="$argument/values"/>
<xsl:value-of select="substring-after($string, $search-string)"/>
</xsl:with-param>
<xsl:with-param name="arguments" select="$arguments[position() > 1]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<OUTPUT>select * from cntwrk where moddte>= 2019-07-24T00:00:01 and ins_dt < 2019-09-23T00:00:01</OUTPUT>
I don't see in your question the logic by which the inserted value needs to be wrapped in todate()so I skipped that part.

Change value in <xsl:value-of select="image"/>

I would like to change an image path.
The code to display the image is:
<xsl:value-of select="image"/>
the image path it takes from the feed is:
https://www.website.com/media/catalog/product/1/7/image.jpg
what i would like is:
https://www.website.com/media/catalog/product/thumbnail/folder/1/7/image.jpg
i tried the following:
<xsl:template name="string-replace-all">
<xsl:param name="text"/>
<xsl:param name="replace"/>
<xsl:param name="with"/>
<xsl:choose>
<xsl:when test="contains($text,$replace)">
<xsl:value-of select="substring-before($text,$replace)"/>
<xsl:value-of select="$with"/>
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="substring-after($text,$replace)"/>
<xsl:with-param name="replace" select="$replace"/>
<xsl:with-param name="with" select="$with"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="image"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="root">
<xsl:for-each select="product[position() = 1]">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="'https://www.website.com/media/catalog/product/'"/>
<xsl:with-param name="replace" select="'product/'" />
<xsl:with-param name="with" select="'product/thumbnail/folder/'"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
the output is:
https://www.website.com/media/catalog/product/thumbnail/folder/https://www.website.com/media/catalog/product/image.jpg
not getting a real error message, it is just showing twice the http part
Couldn't this be a lot simpler? For example, consider:
XML
<root>
<product>
<image>https://www.website.com/media/catalog/product/1/7/image.jpg</image>
</product>
<product>
<image>https://www.website.com/media/catalog/product/2/56/image.jpg</image>
</product>
<product>
<image>https://www.website.com/media/catalog/product/3/123/image.jpg</image>
</product>
</root>
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="/root">
<images>
<xsl:for-each select="product">
<image>
<xsl:text>https://www.website.com/media/catalog/product/thumbnail/folder/</xsl:text>
<xsl:value-of select="substring-after(image, 'https://www.website.com/media/catalog/product/')" />
</image>
</xsl:for-each>
</images>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<images>
<image>https://www.website.com/media/catalog/product/thumbnail/folder/1/7/image.jpg</image>
<image>https://www.website.com/media/catalog/product/thumbnail/folder/2/56/image.jpg</image>
<image>https://www.website.com/media/catalog/product/thumbnail/folder/3/123/image.jpg</image>
</images>

Split a string with "|" Char to columns

I'm a bit new to this language so I have several doubts.
I'm working to process an xml to display some data on pdf form.
But there a few strings that have "|" so I can split the data to display properly.
Here is the example of the input data:
<root>
<reference>
<NroLinRef>12</NroLinRef>
<CodRef>I20</CodRef>
<RazonRef>Data1|Data2|Data3|Data4|Data5|Data6|Data7</RazonRef>
</reference>
</root>
In the output I need something like this so I can display in order in row with cells so data must be clear to read.
<root>
<Reference>
<NroLinRef>12</NroLinRef>
<CodRef>I20</CodRef>
<Data1>Data1</Data1>
<Data2>Data2</Data2>
<Data3>Data3</Data3>
<Data4>Data4</Data4>
<Data5>Data5</Data5>
<Data6>Data6</Data6>
<Data7>Data7</Data7>
</Reference>
</root>
To do this I have been using other code that is from another question but can't find how to get the name to be updated or customized.
And the output I get is actually like this:
<root>
<Reference>
<NroLinRef>12</NroLinRef>
<CodRef>I20</CodRef>
<Data>Data1</Data>
<Data>Data2</Data>
<Data>Data3</Data>
<Data>Data4</Data>
<Data>Data5</Data>
<Data>Data6</Data>
<Data>Data7</Data>
</Reference>
</root>
This is the XSL i'm using
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Referencia/RazonRef" name="tokenize">
<xsl:param name="text" select="."/>
<xsl:param name="separator" select="'|'"/>
<xsl:choose>
<xsl:when test="not(contains($text, $separator))">
<Data>
<xsl:value-of select="normalize-space($text)"/>
</Data>
</xsl:when>
<xsl:otherwise>
<Data>
<xsl:value-of select="normalize-space(substring-before($text, $separator))"/>
</Data>
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="substring-after($text, $separator)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
How can I get the output I want?
The expected result can be achieved by applying 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:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="RazonRef" name="tokenize">
<xsl:param name="text" select="."/>
<xsl:param name="separator" select="'|'"/>
<xsl:param name="i" select="1"/>
<xsl:element name="Data{$i}">
<xsl:value-of select="substring-before(concat($text, $separator), $separator)"/>
</xsl:element>
<xsl:if test="contains($text, $separator)">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="substring-after($text, $separator)"/>
<xsl:with-param name="i" select="$i + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

XSLT Transform to XML

I have some xml in the following format:
<top>
<topValue Value="1#1#5" />
<topValue Value="2#2#10" />
<topValue Value="1#1#3" />
<topValue Value="2#2#30" />
</top>
and output should look like that:
<boo>
<booEnrty>
<v>5</v>
<v>10</v>
</booEnrty>
<booEnrty>
<v>3</v>
<v>30</v>
</booEnrty>
</boo>
my XSLT to transform
<boo>
<xsl:for-each select="top/topValue">
<xsl:if test="position() mod 2 = 0">
<booEnrty>
<v><xsl:value-of select="substring-after(substring-after(#Value,'#'),'#')"/></v>
</booEnrty>
</xsl:if>
</xsl:for-each>
</boo>
What should the XSLT document look like to do this transform?
Any ideas?
Thanks
Maybe someone got a better approach to this, but the XSLT below works for your case.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="top">
<boo>
<xsl:apply-templates select="topValue[position() mod 2 = 1]"/>
</boo>
</xsl:template>
<xsl:template match="topValue[position() mod 2 = 1]">
<booEntry>
<v>
<xsl:call-template name="substring-after-last">
<xsl:with-param name="string" select="#Value" />
<xsl:with-param name="delimiter" select="'#'" />
</xsl:call-template>
</v>
<xsl:apply-templates select="following-sibling::*[1]"/>
</booEntry>
</xsl:template>
<xsl:template match="topValue">
<v>
<xsl:call-template name="substring-after-last">
<xsl:with-param name="string" select="#Value" />
<xsl:with-param name="delimiter" select="'#'" />
</xsl:call-template>
</v>
</xsl:template>
<xsl:template name="substring-after-last">
<xsl:param name="string" />
<xsl:param name="delimiter" />
<xsl:choose>
<xsl:when test="contains($string, $delimiter)">
<xsl:call-template name="substring-after-last">
<xsl:with-param name="string"
select="substring-after($string, $delimiter)" />
<xsl:with-param name="delimiter" select="$delimiter" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise><xsl:value-of select="$string"/></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
How about something short and simple?
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="/top">
<boo>
<xsl:for-each select="topValue[position() mod 2 = 1]">
<booEnrty>
<xsl:for-each select=". | following-sibling::topValue[1]">
<v>
<xsl:value-of select="substring-after(substring-after(#Value,'#'),'#')"/>
</v>
</xsl:for-each>
</booEnrty>
</xsl:for-each>
</boo>
</xsl:template>
</xsl:stylesheet>

Using XSLT to generate nodes based on pipe-delimited attributes

(first off: I'm terribly sorry that you have to look at this document structure; it's hideous)
I have the following XML document:
<MENUS>
<MENU id="192748" servedate="20120213" mealid="3" mealname="Lunch" menuname="Cafeteria" menuid="26" totalcount="200">Cafeteria</MENU>
<RECIPES>
<NUTRIENTS>Calories~Energy (kcal)~kcal|Protein~Protein~gm|Fat~Fat~gm|Carbs~Total Carbohydrates~gm|Cholestrol~Cholesterol~mg|Calcium~Calcium~mg|Sodium~Sodium~mg|Iron~Iron~mg|</NUTRIENTS>
<RECIPE id="6461-200" plucode="" shortname="Chipotle Spinach" numservings="100" portion="4 ounces" isselected="0" ismainitem="0" group="On the Side" publishingdescription="Chipotle Spinach" publishingtext="" enticingdescription="" price="1.53" category="Vegetables" productionarea="Hot Production" nutrients="152|2.3|13.8|6.5|0|74|346|1.85|" nutrientsuncertain="0|0|0|0|0|0|0|0|">Chipotle Spinach,4U</RECIPE>
<RECIPE id="6586-300" plucode="" shortname="Asiago Crusted Chix" numservings="120" portion="3-3/4 ounces" isselected="0" ismainitem="0" group="Main Fare" publishingdescription="Asiago Crusted Chicken" publishingtext="" enticingdescription="" price="2.25" category="Chicken" productionarea="Hot Production" nutrients="203|19.6|7.6|13.2|56|124|387|1.37|" nutrientsuncertain="0|0|0|0|0|0|0|0|">Asiago Crusted Chicken,4U</RECIPE>
<!-- any number of <RECIPE> elements ... -->
</RECIPES>
</MENUS>
The <NUTRIENTS> element contains a pipe-delimited string; the components of this string need to somehow become elements for each <RECIPE>. Furthermore, the values of these new elements are specified by looking at the corresponding position within the pipe-delimited string found in <RECIPE>\<nutrients>.
The overall structure I'm shooting for is:
All of the attributes for a <RECIPE> element are converted into child elements.
The elements of <RECIPE>/<nutrients> map to the same position
within the <NUTRIENTS> element.
Using XSLT 1.0.
So, here would be my expected structure:
<?xml version="1.0"?>
<MENUS>
<MENU id="192748" servedate="20120213" mealid="3" mealname="Lunch" menuname="Cafeteria" menuid="26" totalcount="200">Cafeteria</MENU>
<RECIPES>
<NUTRIENTS>Calories~Energy (kcal)~kcal|Protein~Protein~gm|Fat~Fat~gm|Carbs~Total Carbohydrates~gm|Cholestrol~Cholesterol~mg|Calcium~Calcium~mg|Sodium~Sodium~mg|Iron~Iron~mg|</NUTRIENTS>
<RECIPE>
<id>6461-200</id>
<plucode/>
<shortname>Chipotle Spinach</shortname>
<numservings>100</numservings>
<portion>4 ounces</portion>
<isselected>0</isselected>
<ismainitem>0</ismainitem>
<group>On the Side</group>
<publishingdescription>Chipotle Spinach</publishingdescription>
<publishingtext/>
<enticingdescription/>
<price>1.53</price>
<category>Vegetables</category>
<productionarea>Hot Production</productionarea>
<nutrients>152|2.3|13.8|6.5|0|74|346|1.85|</nutrients>
<nutrientsuncertain>0|0|0|0|0|0|0|0|</nutrientsuncertain>
<CaloriesEnergykcalkcal>152</CaloriesEnergykcalkcal>
<ProteinProteingm>2.3</ProteinProteingm>
<FatFatgm>13.8</FatFatgm>
<CarbsTotalCarbohydrates>6.5</CarbsTotalCarbohydrates>
<CholestrolCholestrolmg>0</CholestrolCholestrolmg>
<CalciumCalciummg>74</CalciumCalciummg>
<SodiumSodiummg>346</SodiumSodiummg>
<IronIronmg>1.85</IronIronmg>
</RECIPE>
<RECIPE>
<id>6586-300</id>
<plucode/>
<shortname>Asiago Crusted Chix</shortname>
<numservings>120</numservings>
<portion>3-3/4 ounces</portion>
<isselected>0</isselected>
<ismainitem>0</ismainitem>
<group>Main Fare</group>
<publishingdescription>Asiago Crusted Chicken</publishingdescription>
<publishingtext/>
<enticingdescription/>
<price>2.25</price>
<category>Chicken</category>
<productionarea>Hot Production</productionarea>
<nutrients>203|19.6|7.6|13.2|56|124|387|1.37|</nutrients>
<nutrientsuncertain>0|0|0|0|0|0|0|0|</nutrientsuncertain>
<CaloriesEnergykcalkcal>203</CaloriesEnergykcalkcal>
<ProteinProteingm>19.6</ProteinProteingm>
<FatFatgm>7.6</FatFatgm>
<CarbsTotalCarbohydrates>13.2</CarbsTotalCarbohydrates>
<CholestrolCholestrolmg>56</CholestrolCholestrolmg>
<CalciumCalciummg>124</CalciumCalciummg>
<SodiumSodiummg>387</SodiumSodiummg>
<IronIronmg>1.37</IronIronmg>
</RECIPE>
<!-- ... -->
</RECIPES>
</MENUS>
(notice, again, that I don't care about the field names we use for these new data points [which begin after <nutrientsuncertain>]; however, bonus points if you would like to show me how to relatively easily specify some sort of array, for lack of a better term, of field names)
Here's my current XSLT, which achieves goal #1; it's goal #2 that I'm stumped on:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- Template #1 - Identity Transform -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- Template #2 - Convert all of a <RECIPE> element's attributes to child elements -->
<xsl:template match="RECIPE/#*">
<xsl:element name="{name()}">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
<!-- Template #3 - Remove extraneous text from each <RECIPE> -->
<xsl:template match="RECIPE/text()" />
</xsl:stylesheet>
That's it. Thanks so much for your help!
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*|#*|text()">
<xsl:copy>
<xsl:apply-templates select="*|#*|text()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="RECIPE">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:variable name="nutrients-table-tmp">
<xsl:call-template name="tokenize-table">
<xsl:with-param name="text" select="../NUTRIENTS/text()"/>
<xsl:with-param name="delimiter-row" select="'|'"/>
<xsl:with-param name="delimiter-col" select="'~'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="nutrients-table" select="exsl:node-set($nutrients-table-tmp)/table"/>
<xsl:variable name="nutrients">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="#nutrients"/>
<xsl:with-param name="delimiter" select="'|'"/>
</xsl:call-template>
</xsl:variable>
<xsl:for-each select="exsl:node-set($nutrients)/token">
<xsl:variable name="pos" select="position()"/>
<xsl:variable name="value" select="text()"/>
<xsl:variable name="row" select="$nutrients-table/row[$pos]"/>
<xsl:variable name="name" select="$row/cell[1]/text()"/>
<xsl:variable name="description" select="$row/cell[2]/text()"/>
<xsl:variable name="unit" select="$row/cell[3]/text()"/>
<xsl:element name="{$name}">
<xsl:attribute name="unit">
<xsl:value-of select="$unit"/>
</xsl:attribute>
<xsl:value-of select="$value"/>
</xsl:element>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="RECIPE/#*">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="text"/>
<xsl:param name="delimiter" select="' '"/>
<xsl:choose>
<xsl:when test="contains($text,$delimiter)">
<token>
<xsl:value-of select="substring-before($text,$delimiter)"/>
</token>
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="substring-after($text,$delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$text">
<token>
<xsl:value-of select="$text"/>
</token>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="tokenize-table">
<xsl:param name="text"/>
<xsl:param name="delimiter-row"/>
<xsl:param name="delimiter-col"/>
<xsl:variable name="rows">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="$text"/>
<xsl:with-param name="delimiter" select="$delimiter-row"/>
</xsl:call-template>
</xsl:variable>
<table>
<xsl:for-each select="exsl:node-set($rows)/token">
<xsl:variable name="items">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="text()"/>
<xsl:with-param name="delimiter" select="$delimiter-col"/>
</xsl:call-template>
</xsl:variable>
<row>
<xsl:for-each select="exsl:node-set($items)/token">
<cell>
<xsl:value-of select="text()"/>
</cell>
</xsl:for-each>
</row>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Output:
<?xml version="1.0" encoding="utf-8"?>
<MENUS>
<MENU id="192748" servedate="20120213" mealid="3" mealname="Lunch" menuname="Cafeteria" menuid="26" totalcount="200">Cafeteria</MENU>
<RECIPES>
<NUTRIENTS>Calories~Energy (kcal)~kcal|Protein~Protein~gm|Fat~Fat~gm|Carbs~Total Carbohydrates~gm|Cholestrol~Cholesterol~mg|Calcium~Calcium~mg|Sodium~Sodium~mg|Iron~Iron~mg|</NUTRIENTS>
<RECIPE>
<id>6461-200</id>
<plucode/>
<shortname>Chipotle Spinach</shortname>
<numservings>100</numservings>
<portion>4 ounces</portion>
<isselected>0</isselected>
<ismainitem>0</ismainitem>
<group>On the Side</group>
<publishingdescription>Chipotle Spinach</publishingdescription>
<publishingtext/>
<enticingdescription/>
<price>1.53</price>
<category>Vegetables</category>
<productionarea>Hot Production</productionarea>
<nutrients>152|2.3|13.8|6.5|0|74|346|1.85|</nutrients>
<nutrientsuncertain>0|0|0|0|0|0|0|0|</nutrientsuncertain>
<Calories unit="kcal">152</Calories>
<Protein unit="gm">2.3</Protein>
<Fat unit="gm">13.8</Fat>
<Carbs unit="gm">6.5</Carbs>
<Cholestrol unit="mg">0</Cholestrol>
<Calcium unit="mg">74</Calcium>
<Sodium unit="mg">346</Sodium>
<Iron unit="mg">1.85</Iron>
</RECIPE>
<RECIPE>
<id>6586-300</id>
<plucode/>
<shortname>Asiago Crusted Chix</shortname>
<numservings>120</numservings>
<portion>3-3/4 ounces</portion>
<isselected>0</isselected>
<ismainitem>0</ismainitem>
<group>Main Fare</group>
<publishingdescription>Asiago Crusted Chicken</publishingdescription>
<publishingtext/>
<enticingdescription/>
<price>2.25</price>
<category>Chicken</category>
<productionarea>Hot Production</productionarea>
<nutrients>203|19.6|7.6|13.2|56|124|387|1.37|</nutrients>
<nutrientsuncertain>0|0|0|0|0|0|0|0|</nutrientsuncertain>
<Calories unit="kcal">203</Calories>
<Protein unit="gm">19.6</Protein>
<Fat unit="gm">7.6</Fat>
<Carbs unit="gm">13.2</Carbs>
<Cholestrol unit="mg">56</Cholestrol>
<Calcium unit="mg">124</Calcium>
<Sodium unit="mg">387</Sodium>
<Iron unit="mg">1.37</Iron>
</RECIPE>
</RECIPES>
</MENUS>
I am not 100% sure I understand your question, but I think you should take a look at the XSL tokenize function. If you combine this in a variable with the position() function you should be able to achieve that correlated output?
To further add to this, you can combine the name() with a (replace for 2.0/translate for 1.0) to get the element name automatically) within a for-each, extracting the positions.
Refer my implementation:-
XSLT File:
<?xml version="1.0" encoding="UTF-8"?>
<!--<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:stylesheet>-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- Template #1 - Identity Transform -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- Template #2 - Convert all of a <RECIPE> element's attributes to child elements -->
<xsl:template match="RECIPE/#*">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<!-- Template #3 - Remove extraneous text from each <RECIPE> -->
<xsl:template match="RECIPE/text()"/>
<!-- Identifying the last attribute -->
<xsl:template match="RECIPE/#*[position()=last()]">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
<!--- Call the String Tokenize template -->
<xsl:call-template name="tokenize">
<xsl:with-param name="string" select="/MENUS/RECIPES/NUTRIENTS/text()"/>
<xsl:with-param name="strValue" select="/MENUS/RECIPES/RECIPE/#nutrients"/>
</xsl:call-template>
</xsl:template>
<!--- String Tokenize -->
<xsl:template name="tokenize">
<xsl:param name="string"/>
<xsl:param name="strValue"/>
<xsl:param name="delimiter" select="'|'"/>
<xsl:choose>
<xsl:when test="$delimiter and contains($string, $delimiter) and contains($strValue, $delimiter)">
<xsl:variable name="subbef" select="translate(substring-before($string, $delimiter), '()~ ', '')"/>
<xsl:text disable-output-escaping="yes"><</xsl:text>
<xsl:value-of select="$subbef"/>
<xsl:text disable-output-escaping="yes">></xsl:text>
<xsl:value-of select="substring-before($strValue, $delimiter)"/>
<xsl:text disable-output-escaping="yes"></</xsl:text>
<xsl:value-of select="$subbef"/>
<xsl:text disable-output-escaping="yes">></xsl:text>
<xsl:call-template name="tokenize">
<xsl:with-param name="string" select="translate(substring-after($string, $delimiter), '()~ ', '')"/>
<xsl:with-param name="strValue" select="substring-after($strValue, $delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:if test="string($string) and string($strValue)">
<xsl:text disable-output-escaping="yes"><</xsl:text>
<xsl:value-of select="$string"/>
<xsl:text disable-output-escaping="yes">></xsl:text>
<xsl:value-of select="$strValue"/>
<xsl:text disable-output-escaping="yes"></</xsl:text>
<xsl:value-of select="$string"/>
<xsl:text disable-output-escaping="yes">></xsl:text>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
OUTPUT:
<?xml version="1.0" encoding="UTF-8"?>
<MENUS>
<MENU id="192748" servedate="20120213" mealid="3" mealname="Lunch" menuname="Cafeteria" menuid="26" totalcount="200">Cafeteria</MENU>
<RECIPES>
<NUTRIENTS>Calories~Energy (kcal)~kcal|Protein~Protein~gm|Fat~Fat~gm|Carbs~Total Carbohydrates~gm|Cholestrol~Cholesterol~mg|Calcium~Calcium~mg|Sodium~Sodium~mg|Iron~Iron~mg|</NUTRIENTS>
<RECIPE>
<id>6461-200</id>
<plucode />
<shortname>Chipotle Spinach</shortname>
<numservings>100</numservings>
<portion>4 ounces</portion>
<isselected>0</isselected>
<ismainitem>0</ismainitem>
<group>On the Side</group>
<publishingdescription>Chipotle Spinach</publishingdescription>
<publishingtext />
<enticingdescription />
<price>1.53</price>
<category>Vegetables</category>
<productionarea>Hot Production</productionarea>
<nutrients>152|2.3|13.8|6.5|0|74|346|1.85|</nutrients>
<nutrientsuncertain>0|0|0|0|0|0|0|0|</nutrientsuncertain>
<CaloriesEnergykcalkcal>152</CaloriesEnergykcalkcal>
<ProteinProteingm>2.3</ProteinProteingm>
<FatFatgm>13.8</FatFatgm>
<CarbsTotalCarbohydratesgm>6.5</CarbsTotalCarbohydratesgm>
<CholestrolCholesterolmg>0</CholestrolCholesterolmg>
<CalciumCalciummg>74</CalciumCalciummg>
<SodiumSodiummg>346</SodiumSodiummg>
<IronIronmg>1.85</IronIronmg>
</RECIPE>
<RECIPE>
<id>6586-300</id>
<plucode />
<shortname>Asiago Crusted Chix</shortname>
<numservings>120</numservings>
<portion>3-3/4 ounces</portion>
<isselected>0</isselected>
<ismainitem>0</ismainitem>
<group>Main Fare</group>
<publishingdescription>Asiago Crusted Chicken</publishingdescription>
<publishingtext />
<enticingdescription />
<price>2.25</price>
<category>Chicken</category>
<productionarea>Hot Production</productionarea>
<nutrients>203|19.6|7.6|13.2|56|124|387|1.37|</nutrients>
<nutrientsuncertain>0|0|0|0|0|0|0|0|</nutrientsuncertain>
<CaloriesEnergykcalkcal>152</CaloriesEnergykcalkcal>
<ProteinProteingm>2.3</ProteinProteingm>
<FatFatgm>13.8</FatFatgm>
<CarbsTotalCarbohydratesgm>6.5</CarbsTotalCarbohydratesgm>
<CholestrolCholesterolmg>0</CholestrolCholesterolmg>
<CalciumCalciummg>74</CalciumCalciummg>
<SodiumSodiummg>346</SodiumSodiummg>
<IronIronmg>1.85</IronIronmg>
</RECIPE>
<!-- any number of <RECIPE> elements ... -->
</RECIPES>
</MENUS>