ce:chem tag to be converted to mml:math - xslt

Is there a way to convert these elsevier tags into mml:math tags?
<ce:chem>PEG<ce:inf>BOUND</ce:inf>
<ce:hsp sp="0.25"/>=<ce:hsp sp="0.25"/>PEG<ce:inf>TOT</ce:inf>
<ce:hsp sp="0.25"/>-<ce:hsp sp="0.25"/>PEG<ce:inf>FINAL</ce:inf>
</ce:chem>

Try this:
XML: (Ensure ce:chem content should not have line breaks and comment text. grouping functions can do better than my code, but I placed this jsut to meet the requirement)
<article xmlns:ce="http://www.elsevier.com/xml/common/dtd"
xmlns:sb="http://www.elsevier.com/xml/common/struct-bib/dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:mml="http://www.w3.org/1998/Math/MathML">
<p>
<ce:chem>PEG<ce:inf>BOUND</ce:inf><ce:hsp sp="0.25"/>=<ce:hsp sp="0.25"/>PEG<ce:inf>TOT</ce:inf><ce:hsp sp="0.25"/>-<ce:hsp sp="0.25"/>PEG<ce:inf>FINAL</ce:inf>A<ce:sup>2</ce:sup></ce:chem>
</p>
</article>
XSLT 2.0: (latest xslt)
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ce="http://www.elsevier.com/xml/common/dtd"
xmlns:sb="http://www.elsevier.com/xml/common/struct-bib/dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:mml="http://www.w3.org/1998/Math/MathML"
xmlns:ja="http://www.elsevier.com/xml/ja/dtd">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()"><xsl:copy><xsl:apply-templates/></xsl:copy></xsl:template>
<xsl:template match="ce:chem">
<xsl:copy>
<xsl:apply-templates select="node()[not(self::ce:inf)][not(self::ce:sup)] | #*" />
</xsl:copy>
</xsl:template>
<xsl:key name="ksub" match="ce:inf" use="generate-id(preceding-sibling::node()[1][self::text()])"/>
<xsl:key name="ksup" match="ce:sup" use="generate-id(preceding-sibling::node()[1][self::text()])"/>
<xsl:template match="ce:chem/text()">
<xsl:choose>
<xsl:when test="following-sibling::node()[1][name()='ce:inf']">
<xsl:element name="ce:msub">
<xsl:call-template name="tempNameElements"/>
<xsl:apply-templates select="key('ksub', generate-id())" />
</xsl:element>
</xsl:when>
<xsl:when test="following-sibling::node()[1][name()='ce:sup']">
<xsl:element name="ce:msup">
<xsl:call-template name="tempNameElements"/>
<xsl:apply-templates select="key('ksup', generate-id())" />
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="tempNameElements"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="ce:hsp">
<xsl:element name="mml:mspace">
<xsl:attribute name="width"><xsl:value-of select="#sp"/></xsl:attribute>
</xsl:element>
</xsl:template>
<xsl:template match="ce:inf">
<xsl:for-each select="node()">
<xsl:call-template name="tempNameElements"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="ce:sup">
<xsl:for-each select="node()">
<xsl:call-template name="tempNameElements"/>
</xsl:for-each>
</xsl:template>
<xsl:template name="tempNameElements">
<xsl:choose>
<xsl:when test="starts-with(replace(self::text(), '([A-z]+)', 'A'), 'A')">
<xsl:element name="mml:mi"><xsl:value-of select="."/></xsl:element>
</xsl:when>
<xsl:when test="starts-with(replace(self::text(), '([0-9]+)', '9'), '9')">
<xsl:element name="mml:mn"><xsl:value-of select="."/></xsl:element>
</xsl:when>
<xsl:when test="matches(self::text(), '^(\(|\[|\{|=|\-)$')">
<xsl:element name="mml:mo"><xsl:value-of select="."/></xsl:element>
</xsl:when>
<xsl:otherwise><xsl:element name="mml:mtext"><xsl:value-of select="."/></xsl:element></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Result:
<?xml version="1.0" encoding="UTF-8"?>
<article xmlns:ce="http://www.elsevier.com/xml/common/dtd"
xmlns:sb="http://www.elsevier.com/xml/common/struct-bib/dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:mml="http://www.w3.org/1998/Math/MathML">
<p>
<ce:chem>
<ce:msub>
<mml:mi>PEG</mml:mi>
<mml:mi>BOUND</mml:mi>
</ce:msub>
<mml:mspace width="0.25"/>
<mml:mo>=</mml:mo>
<mml:mspace width="0.25"/>
<ce:msub>
<mml:mi>PEG</mml:mi>
<mml:mi>TOT</mml:mi>
</ce:msub>
<mml:mspace width="0.25"/>
<mml:mo>-</mml:mo>
<mml:mspace width="0.25"/>
<ce:msub>
<mml:mi>PEG</mml:mi>
<mml:mi>FINAL</mml:mi>
</ce:msub>
<ce:msup>
<mml:mi>A</mml:mi>
<mml:mn>2</mml:mn>
</ce:msup>
</ce:chem>
</p>
</article>

Related

duplicate content in xsl

I am very new in xsl. I was trying to add the <quote> tag in between the <para>.tag. but the output printing twice.
Here is my xsl code
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*" mode="pretrans">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="p[count(child::node())=0]" mode="pretrans"/>
<xsl:template match="doc">
<poc>
<xsl:apply-templates/>
</poc>
</xsl:template>
<xsl:template match="text">
<chapter>
<xsl:variable name="pos" select="count(child::node()[#style='H5']/preceding-sibling::p)+1"/>
<xsl:apply-templates select="child::node()[position()<$pos]" mode="presec"/>
<section>
<xsl:variable name="nodesets" >
<xsl:apply-templates select="child::node()[position()>=$pos]" mode="pretrans"/>
</xsl:variable>
<xsl:apply-templates select="$nodesets" mode="postsec"/> <!---->
</section>
</chapter>
</xsl:template>
<xsl:template match="p" mode="presec">
<xsl:choose>
<xsl:when test="#style='H2'">
<title><xsl:apply-templates/></title>
</xsl:when>
<xsl:when test="#style='H4'">
<subdivision>
<title><xsl:apply-templates/></title>
</subdivision>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="p" mode="postsec">
<xsl:variable name="pos" select="count(preceding-sibling::p[#style='H5'][1]/preceding-sibling::p)+1"/>
<xsl:variable name="pos" select="count(preceding-sibling::p)+1"/>
<xsl:variable name="styleblock" select="count(preceding-sibling::p[#style='BlockStyle'][1]/preceding-sibling::p)+1"/>
<xsl:choose>
<xsl:when test="#style='H5'">
<title><xsl:apply-templates/></title>
</xsl:when>
<xsl:when test="count(child::node())=0"/>
<xsl:otherwise>
<paragraph>
<xsl:if test="#style='BlockStyle'">
<quotes>
<xsl:apply-templates/>
</quotes>
</xsl:if>
<xsl:apply-templates/>
</paragraph>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
expected output:
<poc>
<chapter>
<section>
<paragraph>
<quote>
Hi welcome to new year 2022
</quote>
Hi welcome to new year 2022
</paragraph>
</section>
</chapter>
</poc>
The message is printing twice.
can anyone help me in this.
Depending on your needs delete the first or the second
<paragraph>
<xsl:if test="#style='BlockStyle'">
<quotes>
<xsl:apply-templates/><!-- First -->
</quotes>
</xsl:if>
<xsl:apply-templates/><!-- Second -->
</paragraph>

XSLT - Looping through elements and convert to HTML

My question is traverse through an XML file and find the elements mentioned in variable 1 and replace them with the elements in variable 2 using simplest method.
Sample XML
Below is the sample XML file:
<?xml version="1.0" encoding="utf-8"?>
<root>
<figure id="f0005">
<label>Fig. 1</label>
<caption id="cn0005">
<simple-para id="sp0015">Schematic diagram of the experimental setup.</simple-para>
</caption>
</figure>
<figure id="f0010">
<label>Fig. 2</label>
<caption id="cn0010">
<simple-para id="sp0020">Schematic drawing of the orifice plate.</simple-para>
</caption>
</figure>
</root>
Using this method I am able to get the output. But not the one specified in the required output heading. I think I am doing something wrong in the for loop in the stylesheet. Please share your thoughts.
XSLT
<?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"
version="3.0">
<xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes"/>
<xsl:variable name="els_element_name"
select="tokenize('label simple-para figure caption', '\s+')"/>
<xsl:variable name="html_element_name"
select="tokenize('div div div div div', '\s+')"/>
<xsl:template match="/">
<xsl:for-each select="//element()">
<xsl:variable name="ele_name" select="name()"/>
<xsl:if test="index-of($els_element_name, $ele_name)">
<xsl:variable name="array_val">
<xsl:value-of select="index-of($els_element_name, $ele_name)"/>
</xsl:variable>
<xsl:call-template name="optimized_code_start">
<xsl:with-param name="els_element" select="$ele_name"/>
<xsl:with-param name="position" select="$array_val"/>
</xsl:call-template>
<xsl:value-of select="."/>
<xsl:call-template name="optimized_code_end">
<xsl:with-param name="els_element" select="$ele_name"/>
<xsl:with-param name="position" select="$array_val"/>
</xsl:call-template>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="optimized_code_start">
<xsl:param name="els_element"/>
<xsl:param name="position" as="xs:integer"/>
<xsl:text disable-output-escaping="yes"><</xsl:text>
<xsl:value-of select="$html_element_name[$position]"/>
<xsl:text disable-output-escaping="yes"> class="</xsl:text>
<xsl:value-of select="$els_element"/>
<xsl:text disable-output-escaping="yes">"</xsl:text>
<xsl:for-each select="#*[(name()='id')]">
<xsl:text disable-output-escaping="yes"> </xsl:text>
<xsl:value-of select="name()"/>
<xsl:text disable-output-escaping="yes">="</xsl:text>
<xsl:value-of select="."/>
<xsl:text disable-output-escaping="yes">"</xsl:text>
</xsl:for-each>
<xsl:for-each select="#*[not(name()='id')]">
<xsl:choose>
<xsl:when test="name() = 'xml:lang'">
<xsl:text disable-output-escaping="yes"> </xsl:text>
<xsl:value-of select="name()"/>
<xsl:text disable-output-escaping="yes">="</xsl:text>
<xsl:value-of select="."/>
<xsl:text disable-output-escaping="yes">"</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text disable-output-escaping="yes"> data-</xsl:text>
<xsl:value-of select="name()"/>
<xsl:text disable-output-escaping="yes">="</xsl:text>
<xsl:value-of select="."/>
<xsl:text disable-output-escaping="yes">"</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:text disable-output-escaping="yes">></xsl:text>
</xsl:template>
<xsl:template name="optimized_code_end">
<xsl:param name="els_element"/>
<xsl:param name="position" as="xs:integer"/>
<xsl:text disable-output-escaping="yes"></</xsl:text>
<xsl:value-of select="$html_element_name[$position]"/>
<xsl:text disable-output-escaping="yes">></xsl:text>
</xsl:template>
</xsl:stylesheet>
Required output
I would like to get the output as mentioned below.
<html>
<div class="figure" id="f0005">
<div class="label">Fig. 1</div>
<div class="caption" id="cn0005">
<div class="simple-para" id="sp0015">Schematic diagram of the experimental setup.</div>
</div>
</div>
<div class="figure" id="f0010">
<div class="label">Fig. 2</div>
<div class="caption" id="cn0010">
<div class="simple-para" id="sp0020">Schematic drawing of the orifice plate.
</div>
</div>
</div>
</html>
You can simply use index-of and xsl:element to match as well as map the input names to output names:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:variable name="els_element_name"
select="tokenize('label simple-para figure caption', '\s+')"/>
<xsl:variable name="html_element_name"
select="tokenize('div div div div div', '\s+')"/>
<xsl:template match="*[index-of($els_element_name, name()) > 0]">
<xsl:element name="{$html_element_name[index-of($els_element_name, name(current()))]}">
<xsl:apply-templates select="#*"/>
<xsl:attribute name="class" select="name()"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/bdxtra/2
Another possible approach might be to create a stylesheet implementing the mapping and run it in XSLT 3.0 directly with the transform function:
<?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:axsl="http://www.w3.org/1999/XSL/Transform-alias"
exclude-result-prefixes="#all"
version="3.0">
<xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/>
<xsl:variable name="stylesheet">
<axsl:stylesheet version="3.0">
<axsl:mode on-no-match="shallow-copy"/>
<xsl:for-each select="$els_element_name">
<axsl:template match="{.}">
<axsl:element name="{let $p := position() return $html_element_name[$p]}">
<axsl:apply-templates select="#*"/>
<axsl:attribute name="class" select="'{.}'"/>
<axsl:apply-templates/>
</axsl:element>
</axsl:template>
</xsl:for-each>
</axsl:stylesheet>
</xsl:variable>
<xsl:variable name="els_element_name"
select="tokenize('label simple-para figure caption', '\s+')"/>
<xsl:variable name="html_element_name"
select="tokenize('div div div div div', '\s+')"/>
<xsl:output indent="yes"/>
<xsl:template match="/">
<xsl:sequence select="$stylesheet"/>
<xsl:sequence
select="transform(
map {
'stylesheet-node' : $stylesheet,
'source-node' : .
}
)?output"/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/bdxtra/4. For the real use case remove or comment out the <xsl:sequence select="$stylesheet"/> line but I have kept it in to show the created stylesheet.

Combine space-separated attributes in XSLT

The transform I'm working on mergers two templates that has attributes that are space-separated.
An example would be:
<document template_id="1">
<header class="class1 class2" />
</document>
<document template_id="2">
<header class="class3 class4" />
</document>
And after the transform I want it to be like this:
<document>
<header class="class1 class2 class3 class4" />
</document>
How to achieve this?
I have tried (writing from memory):
<xsl:template match="/">
<header>
<xsl:attribute name="class">
<xsl:for-each select=".//header">
<xsl:value-of select="#class"/>
</xsl:for-each>
</xsl:attribute>
</header>
</xsl:template>
But that appends them all together, but I need them separated... and would be awesome if uniqued as well.
Thank you
Try this template, which simply adds a space before all the headers, apart from the first
<xsl:template match="/">
<header>
<xsl:attribute name="class">
<xsl:for-each select=".//header">
<xsl:if test="position() > 1">
<xsl:text> </xsl:text>
</xsl:if>
<xsl:value-of select="#class"/>
</xsl:for-each>
</xsl:attribute>
</header>
</xsl:template>
If you want your classes without repetitions, then use the following XSLT:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<document>
<xsl:variable name="cls1">
<xsl:for-each select=".//header/#class">
<xsl:call-template name="tokenize">
<xsl:with-param name="txt" select="."/>
</xsl:call-template>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="cls" select="exsl:node-set($cls1)"/>
<header>
<xsl:attribute name="class">
<xsl:for-each select="$cls/*[not(preceding-sibling::* = .)]">
<xsl:value-of select="."/>
<xsl:if test="position() < last()">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:attribute>
</header>
</document>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="txt"/>
<xsl:if test="$txt">
<xsl:if test="contains($txt, ' ')">
<cls>
<xsl:value-of select="substring-before($txt, ' ')"/>
</cls>
<xsl:call-template name="tokenize">
<xsl:with-param name="txt" select="substring-after($txt, ' ')"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="not(contains($txt, ' '))">
<cls>
<xsl:value-of select="$txt"/>
</cls>
</xsl:if>
</xsl:if>
</xsl:template>
</xsl:transform>
In XSLT 1.0 it is much more difficult than in XSLT 2.0, but as you see,
it is possible.
Edit
A revised version using Muenchian grouping (inspired by comment from Michael):
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:variable name="cls1">
<xsl:for-each select=".//header/#class">
<xsl:call-template name="tokenize">
<xsl:with-param name="txt" select="."/>
</xsl:call-template>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="cls2" select="exsl:node-set($cls1)"/>
<xsl:key name="classKey" match="cls" use="."/>
<xsl:template match="/">
<document>
<header>
<xsl:attribute name="class">
<xsl:for-each select="$cls2/*[generate-id() = generate-id(key('classKey', .)[1])]">
<xsl:value-of select="."/>
<xsl:if test="position() < last()">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:attribute>
</header>
</document>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="txt"/>
<xsl:if test="$txt">
<xsl:if test="contains($txt, ' ')">
<cls>
<xsl:value-of select="substring-before($txt, ' ')"/>
</cls>
<xsl:call-template name="tokenize">
<xsl:with-param name="txt" select="substring-after($txt, ' ')"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="not(contains($txt, ' '))">
<cls>
<xsl:value-of select="$txt"/>
</cls>
</xsl:if>
</xsl:if>
</xsl:template>
</xsl:transform>

wrap XSLT result in a CDATA block

I doing an XSLT transformation.
input message:
<Accounts operation="query">
<Account operation="query">
<Home_spcPage>google.com</Home_spcPage>
<Id>1-NP8S</Id>
</Account>
</Accounts>
which should get transformed to :
<ipString>
<![CDATA[<Accounts operation="update" boNameVar="Account" bcNameVar="Account">
<Account operation="update">
<Home_spcPage>google.com</Home_spcPage>
<Id>1-NP8S</Id>
</Account>
</Accounts>]]>
</ipString>
I am trying with the below XSLT.
<xsl:stylesheet xmlns:crma="www.c123.com" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output indent="yes" method="xml"/>
<xsl:variable name="messageBlock">
<xsl:call-template name="main"/>
</xsl:variable>
<xsl:template match="/" name="main">
<xsl:apply-templates select="#*|node()"/>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:variable name="level" select="count(ancestor::node())"/>
<xsl:copy>
<xsl:choose>
<xsl:when test="$level=2">
<xsl:attribute name="operation">
<xsl:value-of select="'update'"/>
</xsl:attribute>
<xsl:variable name="currNodeVar" select="name()"/>
<xsl:if test="$currNodeVar='Account'">
<xsl:attribute name="boNameVar">Account</xsl:attribute>
<xsl:attribute name="bcNameVar">Account</xsl:attribute>
</xsl:if>
<xsl:if test="$currNodeVar='Contact'">
<xsl:attribute name="boNameVar">Contact</xsl:attribute>
<xsl:attribute name="bcNameVar">Contact</xsl:attribute>
</xsl:if>
</xsl:when>
<xsl:when test="$level=4">
<xsl:attribute name="operation">
<xsl:value-of select="'update'"/>
</xsl:attribute>
<xsl:variable name="currBCNameVar" select="name()"/>
<xsl:variable name="parBCNameVar" select="name(../..)"/>
</xsl:when>
</xsl:choose>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:copy-of select="$messageBlock"/>
</xsl:template>
<xsl:template match="/">
<xsl:element name="ipString">
<xsl:text disable-output-escaping="yes"><![CDATA[</xsl:text>
<xsl:copy-of select="$messageBlock"/>
<xsl:text disable-output-escaping="yes">]]></xsl:text>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
but not getting the desired results . It seems like the XSLT is not considering the entire XML.
Any help is appreciated .
Thanks,
Naveen

Append multiple tags on same level if a specific tag is already there

I changed now multiple times my xsl, but I didn't find the right way. Now here is what I want:
I try to find now all missing Pages, which got no description. If it's missing, I want to add a description for that page, if it exist, I want to modify the Description. The String for pageX to pageXDescription is always the same.
Here is my short example xml:
<BOOK>
<PAGE NAME='page1' VALUE='coolText'/>
<PAGE NAME='Description1' VALUE='coolDescription'/>
<PAGE NAME='page2' VALUE='moreText'/>
<PAGE NAME='page3' VALUE='aLotMoreText'/>
<PAGE NAME='Description3' VALUE='aLotMoreDescriptions'/>
</BOOK>
I tried it with something like that:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- define output settings and header -->
<xsl:output method="xml" indent="yes" omit-xml-declaration="no" media-type="string" encoding="ISO-8859-1" doctype-system="deftable.dtd"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="BOOK[PAGE/#NAME='page1']">
<xsl:copy>
<xsl:call-template name="create_missing_description_pages">
<xsl:with-param name="page" select="'page1'"/>
<xsl:with-param name="description" select="'Description1'"/>
<xsl:with-param name="new_description" select="'newContent'"/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:template match="BOOK[PAGE/#NAME='page2']">
<xsl:copy>
<xsl:call-template name="create_missing_description_pages">
<xsl:with-param name="page" select="'page2'"/>
<xsl:with-param name="description" select="'Description2'"/>
<xsl:with-param name="new_description" select="'newContent'"/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:template match="BOOK[PAGE/#NAME='page3']">
<xsl:copy>
<xsl:call-template name="create_missing_description_pages">
<xsl:with-param name="page" select="'page3'"/>
<xsl:with-param name="description" select="'Description3'"/>
<xsl:with-param name="new_description" select="'newContent'"/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<!-- Function to generate missing XML Tags -->
<xsl:template name="create_missing_description_pages">
<xsl:param name="page"/>
<xsl:param name="description"/>
<xsl:param name="new_description"/>
<xsl:apply-templates select="#*|VARIABLE[#NAME=$page]/preceding-sibling::node()"/>
<xsl:apply-templates select="VARIABLE[#NAME=$page]"/>
<xsl:if test="not(VARIABLE/#NAME=$description)">
<xsl:element name="PAGE">
<xsl:attribute name="NAME"><xsl:value-of select="$description"/></xsl:attribute>
<xsl:attribute name="VALUE"><xsl:value-of select="$new_description"/></xsl:attribute>
</xsl:element>
</xsl:if>
<xsl:apply-templates select="VARIABLE[#NAME=$page]/following-sibling::node()"/>
</xsl:template>
<xsl:template match="BOOK/PAGE">
<xsl:copy>
<xsl:choose>
<xsl:when test="#NAME='Description1'">
<xsl:attribute name="NAME"><xsl:value-of select="#NAME"/></xsl:attribute>
<xsl:attribute name="VALUE">newContent</xsl:attribute>
</xsl:when>
<xsl:when test="#NAME='Description2'">
<xsl:attribute name="NAME"><xsl:value-of select="#NAME"/></xsl:attribute>
<xsl:attribute name="VALUE">newContent</xsl:attribute>
</xsl:when>
<xsl:when test="#NAME='page3Description'">
<xsl:attribute name="NAME"><xsl:value-of select="#NAME"/></xsl:attribute>
<xsl:attribute name="VALUE">newContent</xsl:attribute>
</xsl:when>
<!-- other child items will just be copied -->
<xsl:otherwise>
<xsl:copy-of select="#*"/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
That is what I'm expecting in all cases, if all Descriptions are missing, or if all Descriptions are available:
<BOOK>
<PAGE NAME='page1' VALUE='coolText'/>
<PAGE NAME='Description1' VALUE='newContent'/>
<PAGE NAME='page2' VALUE='moreText'/>
<PAGE NAME='Description2' VALUE='newContent'/>
<PAGE NAME='page3' VALUE='aLotMoreText'/>
<PAGE NAME='Description3' VALUE='newContent'/>
</BOOK>
If there is no page3, then I want also no page Description.
I hope so, that it's understandable.
Thanks a lot for a hint, where my logical failure is and how to fix it.
Best regards
Björn
Couldn't you make this simply:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<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="PAGE">
<xsl:variable name="pagenum" select="xs:integer(substring-after(#NAME, 'page'))" />
<xsl:copy-of select="."/>
<PAGE NAME='Description{$pagenum}'>
<xsl:attribute name="VALUE">
<xsl:choose>
<xsl:when test="$pagenum=1">newContent1</xsl:when>
<xsl:when test="$pagenum=2">newContent2</xsl:when>
<xsl:when test="$pagenum=3">newContent3</xsl:when>
</xsl:choose>
</xsl:attribute>
</PAGE>
</xsl:template>
<xsl:template match="PAGE[starts-with(#NAME, 'Description')]"/>
</xsl:stylesheet>