I would like to retrieve a value of my xml file with a xsl template but I have no idea how to retrieve these value...
I have this XML file
<BlastOutput_iterations>
<Iteration>
<Iteration_iter-num>1</Iteration_iter-num>
<Iteration_query-ID>Query_1</Iteration_query-ID>
<Iteration_query-def>Teg18as_antisens</Iteration_query-def>
<Iteration_query-len>865</Iteration_query-len>
<Iteration_hits>
<Hit>
<Hit_num>1</Hit_num>
<Hit_id>gnl|BL_ORD_ID|30</Hit_id>
<Hit_def>gi|150392480|ref|NC_009632.1| Staphylococcus aureus subsp. aureus JH1, complete genome</Hit_def>
<Hit_accession>2233</Hit_accession>
<Hit_len>2906507</Hit_len>
<Hit_hsps>
<Hsp>
<Hsp_num>1</Hsp_num>
<Hsp_bit-score>1561.20031714635</Hsp_bit-score>
<Hsp_score>1730</Hsp_score>
<Hsp_evalue>0</Hsp_evalue>
<Hsp_query-from>1</Hsp_query-from>
<Hsp_query-to>865</Hsp_query-to>
<Hsp_hit-from>355668</Hsp_hit-from>
<Hsp_hit-to>356532</Hsp_hit-to>
<Hsp_query-frame>1</Hsp_query-frame>
<Hsp_hit-frame>1</Hsp_hit-frame>
<Hsp_identity>865</Hsp_identity>
<Hsp_positive>865</Hsp_positive>
<Hsp_gaps>0</Hsp_gaps>
<Hsp_align-len>865</Hsp_align-len>
<Hsp_qseq>CAACTCGTTAGGACAATCACGATGATTGTCTACAGTTGCAGGTGGATTTGAATATACTACTAGTTATTTGTTGTCTAGGATAATAGATTTAGTATGTTGATAAGTTTGACTCAGATTTGTATTTTCTAATAAATGATAACTCACGATATCGATTAAAAAGAGTGTCGCAATTTGTGTGTTGATAAATTGATGGTCGGTATTACGCGATTGATCCGTTGTTAAAAGTACTAAATCTGCACAATCTGTAAGTTTACTACCTTCGAAATTTGTGATGGCAACGACATATGCACCATGAGATTTGGCGACTTCCGCTGCTGAAATTAATTCCGAAGTATTACCACTATTTGACATAGCAATAAACATGTCCGAATGAGATAGTAGGGATGCCGATATTTTCATTAAATGTGAATCGGTAGTAACATTACCTTTTAGCCCCATACGAATCATACGATAATAAAATTCAGTCGCTGATAAACCAGAGCTACCTAGTCCAGCAAAGAGTATATGTCGACTTGATTGGAGTTTGTCGATAAAGGTTTGGATAATGTCGTTATCAATAAATTCACCAGTTTGTTGAATGATTTGTTGATGATATTTATGAATTCTTTGAATAATTGGGCTATTTTCAATAACTGTCTCTGTCATTTCTTGTTGAATATTAAATTTTAAATCTTGGAAATTCTCATAATCTAGCTTATGACTAAAGCGTGTCATCGTTGCTGGTGATGTACCAATCGCATGGGCTAAGGAGTTAATCGTTGAAAAGGCATCGCTATAACCATTTTGTCTTATATAATTGACGATGCGTTTATCAGTTTTTGTAAATAAATGTTGATAACGTTGAACACGATTCTCAAATTTCATT</Hsp_qseq>
<Hsp_hseq>CAACTCGTTAGGACAATCACGATGATTGTCTACAGTTGCAGGTGGATTTGAATATACTACTAGTTATTTGTTGTCTAGGATAATAGATTTAGTATGTTGATAAGTTTGACTCAGATTTGTATTTTCTAATAAATGATAACTCACGATATCGATTAAAAAGAGTGTCGCAATTTGTGTGTTGATAAATTGATGGTCGGTATTACGCGATTGATCCGTTGTTAAAAGTACTAAATCTGCACAATCTGTAAGTTTACTACCTTCGAAATTTGTGATGGCAACGACATATGCACCATGAGATTTGGCGACTTCCGCTGCTGAAATTAATTCCGAAGTATTACCACTATTTGACATAGCAATAAACATGTCCGAATGAGATAGTAGGGATGCCGATATTTTCATTAAATGTGAATCGGTAGTAACATTACCTTTTAGCCCCATACGAATCATACGATAATAAAATTCAGTCGCTGATAAACCAGAGCTACCTAGTCCAGCAAAGAGTATATGTCGACTTGATTGGAGTTTGTCGATAAAGGTTTGGATAATGTCGTTATCAATAAATTCACCAGTTTGTTGAATGATTTGTTGATGATATTTATGAATTCTTTGAATAATTGGGCTATTTTCAATAACTGTCTCTGTCATTTCTTGTTGAATATTAAATTTTAAATCTTGGAAATTCTCATAATCTAGCTTATGACTAAAGCGTGTCATCGTTGCTGGTGATGTACCAATCGCATGGGCTAAGGAGTTAATCGTTGAAAAGGCATCGCTATAACCATTTTGTCTTATATAATTGACGATGCGTTTATCAGTTTTTGTAAATAAATGTTGATAACGTTGAACACGATTCTCAAATTTCATT</Hsp_hseq>
<Hsp_midline>|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||</Hsp_midline>
</Hsp>
</Hit_hsps>
</Hit>
</Iteration_hits>
</Iteration>
and a would like to retrieve NC_009632.1 in gi|150392480|ref|NC_009632.1| Staphylococcus aureus subsp. aureus JH1, complete genome, the structure of these line is always like "xx|number|xxx|value_to_retrieve| "
thank for help
<xsl:template match="Hit_def">
<xsl:value-of select="substring-before(
substring-after(
substring-after(
substring-after(., '|'),
'|'
),
'|'
),
'|'
)"/>
</xsl:template>
with XSLT 1.0 should do, with 2.0
<xsl:template match="Hit_def">
<xsl:value-of select="tokenize(., '|')[4]"/>
</xsl:template>
is easier.
Here is a simple tokenization. One can use the result to access any token by position:
<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="text()" name="tokenize">
<xsl:param name="pText" select="concat(.,'|')"/>
<xsl:if test="string-length($pText)">
<t>
<xsl:value-of select="substring-before($pText, '|')"/>
</t>
<xsl:call-template name="tokenize">
<xsl:with-param name="pText" select=
"substring-after($pText, '|')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on:
<t>a|b|c|d|e|f|g|h</t>
produces:
<t>a</t>
<t>b</t>
<t>c</t>
<t>d</t>
<t>e</t>
<t>f</t>
<t>g</t>
<t>h</t>
Finally, we are using this tokenisation technique to solve the original problem:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" >
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vrtfTokens">
<xsl:call-template name="tokenize">
<xsl:with-param name="pText" select=
"/*/*/*/*/Hit_def"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="ext:node-set($vrtfTokens)/*[4]"/>
</xsl:template>
<xsl:template match="text()" name="tokenize">
<xsl:param name="pText" select="concat(.,'|')"/>
<xsl:if test="string-length($pText)">
<t>
<xsl:value-of select="substring-before($pText, '|')"/>
</t>
<xsl:call-template name="tokenize">
<xsl:with-param name="pText" select=
"substring-after($pText, '|')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the provided XML document:
<BlastOutput_iterations>
<Iteration>
<Iteration_iter-num>1</Iteration_iter-num>
<Iteration_query-ID>Query_1</Iteration_query-ID>
<Iteration_query-def>Teg18as_antisens</Iteration_query-def>
<Iteration_query-len>865</Iteration_query-len>
<Iteration_hits>
<Hit>
<Hit_num>1</Hit_num>
<Hit_id>gnl|BL_ORD_ID|30</Hit_id>
<Hit_def>gi|150392480|ref|NC_009632.1| Staphylococcus aureus subsp. aureus JH1, complete genome</Hit_def>
<Hit_accession>2233</Hit_accession>
<Hit_len>2906507</Hit_len>
<Hit_hsps>
<Hsp>
<Hsp_num>1</Hsp_num>
<Hsp_bit-score>1561.20031714635</Hsp_bit-score>
<Hsp_score>1730</Hsp_score>
<Hsp_evalue>0</Hsp_evalue>
<Hsp_query-from>1</Hsp_query-from>
<Hsp_query-to>865</Hsp_query-to>
<Hsp_hit-from>355668</Hsp_hit-from>
<Hsp_hit-to>356532</Hsp_hit-to>
<Hsp_query-frame>1</Hsp_query-frame>
<Hsp_hit-frame>1</Hsp_hit-frame>
<Hsp_identity>865</Hsp_identity>
<Hsp_positive>865</Hsp_positive>
<Hsp_gaps>0</Hsp_gaps>
<Hsp_align-len>865</Hsp_align-len>
<Hsp_qseq>CAACTCGTTAGGACAATCACGATGATTGTCTACAGTTGCAGGTGGATTTGAATATACTACTAGTTATTTGTTGTCTAGGATAATAGATTTAGTATGTTGATAAGTTTGACTCAGATTTGTATTTTCTAATAAATGATAACTCACGATATCGATTAAAAAGAGTGTCGCAATTTGTGTGTTGATAAATTGATGGTCGGTATTACGCGATTGATCCGTTGTTAAAAGTACTAAATCTGCACAATCTGTAAGTTTACTACCTTCGAAATTTGTGATGGCAACGACATATGCACCATGAGATTTGGCGACTTCCGCTGCTGAAATTAATTCCGAAGTATTACCACTATTTGACATAGCAATAAACATGTCCGAATGAGATAGTAGGGATGCCGATATTTTCATTAAATGTGAATCGGTAGTAACATTACCTTTTAGCCCCATACGAATCATACGATAATAAAATTCAGTCGCTGATAAACCAGAGCTACCTAGTCCAGCAAAGAGTATATGTCGACTTGATTGGAGTTTGTCGATAAAGGTTTGGATAATGTCGTTATCAATAAATTCACCAGTTTGTTGAATGATTTGTTGATGATATTTATGAATTCTTTGAATAATTGGGCTATTTTCAATAACTGTCTCTGTCATTTCTTGTTGAATATTAAATTTTAAATCTTGGAAATTCTCATAATCTAGCTTATGACTAAAGCGTGTCATCGTTGCTGGTGATGTACCAATCGCATGGGCTAAGGAGTTAATCGTTGAAAAGGCATCGCTATAACCATTTTGTCTTATATAATTGACGATGCGTTTATCAGTTTTTGTAAATAAATGTTGATAACGTTGAACACGATTCTCAAATTTCATT</Hsp_qseq>
<Hsp_hseq>CAACTCGTTAGGACAATCACGATGATTGTCTACAGTTGCAGGTGGATTTGAATATACTACTAGTTATTTGTTGTCTAGGATAATAGATTTAGTATGTTGATAAGTTTGACTCAGATTTGTATTTTCTAATAAATGATAACTCACGATATCGATTAAAAAGAGTGTCGCAATTTGTGTGTTGATAAATTGATGGTCGGTATTACGCGATTGATCCGTTGTTAAAAGTACTAAATCTGCACAATCTGTAAGTTTACTACCTTCGAAATTTGTGATGGCAACGACATATGCACCATGAGATTTGGCGACTTCCGCTGCTGAAATTAATTCCGAAGTATTACCACTATTTGACATAGCAATAAACATGTCCGAATGAGATAGTAGGGATGCCGATATTTTCATTAAATGTGAATCGGTAGTAACATTACCTTTTAGCCCCATACGAATCATACGATAATAAAATTCAGTCGCTGATAAACCAGAGCTACCTAGTCCAGCAAAGAGTATATGTCGACTTGATTGGAGTTTGTCGATAAAGGTTTGGATAATGTCGTTATCAATAAATTCACCAGTTTGTTGAATGATTTGTTGATGATATTTATGAATTCTTTGAATAATTGGGCTATTTTCAATAACTGTCTCTGTCATTTCTTGTTGAATATTAAATTTTAAATCTTGGAAATTCTCATAATCTAGCTTATGACTAAAGCGTGTCATCGTTGCTGGTGATGTACCAATCGCATGGGCTAAGGAGTTAATCGTTGAAAAGGCATCGCTATAACCATTTTGTCTTATATAATTGACGATGCGTTTATCAGTTTTTGTAAATAAATGTTGATAACGTTGAACACGATTCTCAAATTTCATT</Hsp_hseq>
<Hsp_midline>|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||</Hsp_midline>
</Hsp>
</Hit_hsps>
</Hit>
</Iteration_hits>
</Iteration>
</BlastOutput_iterations>
the wanted, correct result is produced:
NC_009632.1
Related
This is my template (a simplification of a real situation):
<xsl:template name="i">
<xsl:param name="args"/>
<xsl:for-each select="$args/*">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
I call it like this:
<xsl:template match="f">
<xsl:call-template name="i">
<xsl:with-param name="args"/>
<a><xsl:value-of select="./#one"/></a>
<a><xsl:value-of select="./#two"/></a>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
Basically, I create artificial nodes <a/>, which looks ugly to me. Is there a better way?
My example would use functions but I have done both:
<?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="#all"
version="3.0">
<xsl:function name="mf:f1" as="text()">
<xsl:param name="items" as="item()*"/>
<xsl:value-of select="$items"/>
</xsl:function>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="foo">
<function-example>
<xsl:sequence select="mf:f1((#one, #two))"/>
</function-example>
<template-example1>
<xsl:call-template name="h">
<xsl:with-param name="args" select="#one, #two"/>
</xsl:call-template>
</template-example1>
<template-example2>
<xsl:call-template name="i">
<xsl:with-param name="args" select="#one, #two"/>
</xsl:call-template>
</template-example2>
</xsl:template>
<xsl:template name="h">
<xsl:param name="args"/>
<xsl:value-of select="$args"/>
</xsl:template>
<xsl:template name="i">
<xsl:param name="args"/>
<xsl:for-each select="$args">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
transforms
<foo one="1" two="2"/>
into
<function-example>1 2</function-example>
<template-example1>1 2</template-example1>
<template-example2>12</template-example2>
https://xsltfiddle.liberty-development.net/jyfAiD9
Of course, for the sole xsl:value-of use, if you don't want to have the default space separator between the args values, use xsl:value-of separator="".
Consider the following simple example:
XML
<f one="alpha" two="bravo"/>
template call
<xsl:template match="f">
<xsl:call-template name="i">
<xsl:with-param name="args" select="#one, #two"/>
</xsl:call-template>
</xsl:template>
template execution
<xsl:template name="i">
<xsl:param name="args"/>
<xsl:for-each select="$args">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
Demo: https://xsltfiddle.liberty-development.net/93PXKr2
This is my xml:
<A>
<D>dd</D>
<E>ee</E>
<B>
<C>1</C>
<C>2</C>
</B>
</A>
This was my xsl. I had to call template1 and template2 only few times for the same params:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/A">
<xsl:call-template name="items1">
<xsl:with-param name="key">aa</xsl:with-param>
<xsl:with-param name="value">bb</xsl:with-param>
</xsl:call-template>
<xsl:call-template name="items2">
<xsl:with-param name="key">aa</xsl:with-param>
<xsl:with-param name="value">bb</xsl:with-param>
</xsl:call-template>
...
</xsl:template>
<xsl:template name="template1 ">
<xsl:param name="key"/>
<xsl:param name="value"/>
<xsl:value-of select="/D"/>
<xsl:value-of select="$key"/>
<xsl:value-of select="$value"/>
...
</xsl:template>
<xsl:template name="template2">
<xsl:param name="key"/>
<xsl:param name="value"/>
<xsl:value-of select="/E"/>
<xsl:value-of select="$key"/>
<xsl:value-of select="$value"/>
...
</xsl:template>
</xsl:stylesheet>
Now, I have to call my templates about 350 times for template1 and 350 times for template2, I have many key, value pairs.
I thought to make one dictionary with key value pairs and iterate over it on template1 and template2
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:variable name="values">
<item key="A" value="aa"/>
<item key="B" value="bb"/>
...
</xsl:variable>
<xsl:template match="/A">
<xsl:for-each select="$values">
<xsl:call-template name="items1">
<xsl:with-param name="key">aa</xsl:with-param>
<xsl:with-param name="value">bb</xsl:with-param>
</xsl:call-template>
</xsl:for-each>
<xsl:for-each select="$values">
<xsl:call-template name="items2">
<xsl:with-param name="key">aa</xsl:with-param>
<xsl:with-param name="value">bb</xsl:with-param>
</xsl:call-template>
</xsl:for-each>
...
</xsl:template>
<xsl:template name="template1 ">
<xsl:param name="key"/>
<xsl:param name="value"/>
<xsl:value-of select="/D"/>
<xsl:value-of select="$key"/>
<xsl:value-of select="$value"/>
...
</xsl:template>
<xsl:template name="template2">
<xsl:param name="key"/>
<xsl:param name="value"/>
<xsl:value-of select="/E"/>
<xsl:value-of select="$key"/>
<xsl:value-of select="$value"/>
...
</xsl:template>
</xsl:stylesheet>
My question is that Is it good idea to solve my problem. How to get root node in templates, now current node is "item". I wanted to get value od D node at my template.
It's difficult to answer your question without seeing the complete picture. Without it I can only guess you want to do:
<xsl:for-each select="$prodDict/item">
<xsl:call-template name="Item">
<xsl:with-param name="param1" select="#key"/>
<xsl:with-param name="param" select="#value"/>
</xsl:call-template>
</xsl:for-each>
Note that this requires XSLT 2.0 or higher.
Added:
How to get root node in templates, now current node is "item".
A convenient method is to place the root node in a global variable, so that it's accessible from anywhere. Here's a simplified example:
XML
<A>
<D>dd</D>
<E>ee</E>
<B>
<C>1</C>
<C>2</C>
</B>
</A>
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="values">
<item key="A" value="aa"/>
<item key="B" value="bb"/>
</xsl:variable>
<xsl:variable name="xml" select="/" />
<xsl:template match="/">
<result>
<xsl:for-each select="$values/item">
<xsl:call-template name="template1">
<xsl:with-param name="param1" select="#key"/>
<xsl:with-param name="param2" select="#value"/>
</xsl:call-template>
</xsl:for-each>
</result>
</xsl:template>
<xsl:template name="template1 ">
<xsl:param name="param1"/>
<xsl:param name="param2"/>
<item>
<d>
<xsl:value-of select="$xml/A/D"/>
</d>
<key>
<xsl:value-of select="$param1"/>
</key>
<value>
<xsl:value-of select="$param2"/>
</value>
</item>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<result>
<item>
<d>dd</d>
<key>A</key>
<value>aa</value>
</item>
<item>
<d>dd</d>
<key>B</key>
<value>bb</value>
</item>
</result>
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>
A customer supplied XML contains the delivery address as a comma separated string, I would like to split this string into named nodes using XSLT 1.0.
<?xml version="1.0" encoding="UTF-8"?>
<root>
<text>
Company, Streetaddress 20, 1234 AA, City
</text>
</root>
Output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<text>
<COMPANY>Company</COMPANY>
<ADDRESS>Streetaddress 20</ADDRESS>
<ZIPCODE>1234 AA</ZIPCODE>
<CITY>City</CITY>
</text>
</root>
I tried several recursive templates for XSLT 1.0 which do a fine job splitting but the resulting nodes are identically named.
If possible, how can this be achieved using XSLT 1.0?
Does it have to be a recursive template? How about a straight-forward chain of substring-before and substring-after like this:
<xsl:template match="text">
<xsl:copy>
<COMPANY>
<xsl:value-of select="normalize-space(substring-before(., ','))"/>
</COMPANY>
<xsl:variable name="s1" select="substring-after(., ',')"/>
<ADDRESS>
<xsl:value-of select="normalize-space(substring-before($s1, ','))"/>
</ADDRESS>
<xsl:variable name="s2" select="substring-after($s1, ',')"/>
<ZIPCODE>
<xsl:value-of select="normalize-space(substring-before($s2, ','))"/>
</ZIPCODE>
<CITY>
<xsl:value-of select="normalize-space(substring-after($s2, ','))"/>
</CITY>
</xsl:copy>
</xsl:template>
For the fun of it, here is a generic version using a recursive template.
<xsl:template match="text">
<xsl:copy>
<xsl:call-template name="parse-comma-separated">
<xsl:with-param name="elements" select="'COMPANY,ADDRESS,ZIPCODE,CITY'"/>
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:template name="parse-comma-separated">
<xsl:param name="elements"/>
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($elements, ',')">
<xsl:element name="{normalize-space(substring-before($elements, ','))}">
<xsl:value-of select="normalize-space(substring-before($text, ','))"/>
</xsl:element>
<xsl:call-template name="parse-comma-separated">
<xsl:with-param name="elements" select="substring-after($elements, ',')"/>
<xsl:with-param name="text" select="substring-after($text, ',')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{normalize-space($elements)}">
<xsl:value-of select="normalize-space($text)"/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
As the supplied XML only had the address as single node (Streettaddress 20) I added a second variable to split the address string into streetaddress and housenumber.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" indent="yes" method="xml"/>
<xsl:template match="text">
<root>
<COMPANY>
<xsl:value-of select="normalize-space(substring-before(., ','))"/>
</COMPANY>
<xsl:variable name="s1" select="substring-after(., ',')"/>
<xsl:variable name="address_temp" select="normalize-space(substring-before($s1, ','))"/>
<ADDRESS>
<xsl:value-of select="normalize-space(substring-before($address_temp, ' '))"/>
</ADDRESS>
<HOUSENUMBER>
<xsl:value-of select="normalize-space(substring-after($address_temp, ' '))"/>
</HOUSENUMBER>
<xsl:variable name="s2" select="substring-after($s1, ',')"/>
<ZIPCODE>
<xsl:value-of select="normalize-space(substring-before($s2, ','))"/>
</ZIPCODE>
<CITY>
<xsl:value-of select="normalize-space(substring-after($s2, ','))"/>
</CITY>
</root>
</xsl:template>
</xsl:stylesheet>
Result:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<COMPANY>Company</COMPANY>
<ADDRESS>Streetaddress</ADDRESS>
<HOUSENUMBER>20</HOUSENUMBER>
<ZIPCODE>1234 AA</ZIPCODE>
<CITY>City</CITY>
</root>
(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>