Search node/text() from (Result Tree Fragment) - xslt

my templates (XSLT 1.0)
<xsl:template name="doo">
<xsl:variable name="nodelist">
<root>
<a size="12" number="11">
<sex>male</sex>
Hulk
</a>
<a size="12" number="11">
<sex>male</sex>
Steven XXXXXXXXXXX
</a>
</root>
</variable>
<xsl:call-template name="findString">
<xsl:with-param name="content1" select="$nodelist"></xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="findString">
<xsl:param name="content1" select="."></xsl:param>
<!-- here i need to search the text() XXXXXXXXXXX from $content1 and replace them-->
</xsl:template>
is this possible like
for each node in Tree Fragment from myvariable
if node/text()='xxxxxxxx'
do something

With xslt version=1.0 you can use a extension "not-set".
<xsl:call-template name="findString">
<xsl:with-param name="content1" select="exsl:node-set($nodelist)"></xsl:with-param>
</xsl:call-template>
To make it woke you have to add following lines.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl"
version="1.0">
Update:
Based on solution from Mads Hansen
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:template name="doo">
<xsl:variable name="nodelist">
<root>
<a size="12" number="11">
<sex>male</sex>
Hulk
</a>
<a size="12" number="11">
<sex>male</sex>
Steven XXXXXXXXXXX
</a>
</root>
</xsl:variable>
aaa
<xsl:call-template name="findString">
<xsl:with-param name="content1" select="exsl:node-set($nodelist)"></xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template match="/">
<xsl:call-template name="doo" />
</xsl:template>
<xsl:template name="findString">
<xsl:param name="content1" select="."></xsl:param>
<!-- here i need to search the text() XXXXXXXXXXX from $content1 and replace them-->
<xsl:apply-templates mode="mytext" select="$content1"/>
</xsl:template>
<!--Identity template will copy all matched nodes and apply-templates-->
<xsl:template match="#*|node()" mode="mytext">
<xsl:copy>
<xsl:apply-templates select="#*|node()" mode="mytext"/>
</xsl:copy>
</xsl:template>
<!--Specialized template to match on text nodes that contain the "findString" value-->
<xsl:template match="text()[contains(.,'XXXXXXXXXXX')]" mode="mytext">
<xsl:variable name="findString" select="'XXXXXXXXXXX'"/>
<xsl:variable name="replaceString" select="'YYYYYYYYYYYY'"/>
<xsl:value-of select="concat(substring-before(., $findString),
$replaceString,
substring-after(., $findString))"/>
</xsl:template>
</xsl:stylesheet>

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>

How we import the value in one xsl into another xsl in XSLT1.0

I have below xsl created Address1.xsl
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0">
<xsl:param name="FCode"/>
<xsl:param name="FName"/>
<xsl:template match="/">
<xsl:call-template name="Addtemplate"/>
</xsl:template>
<xsl:template name=" Addtemplate ">
<xsl:variable name="TownType">
<xsl:if test="$FCode!= ''">
<xsl:value-of select="ABCValue"/>
</xsl:if>
</xsl:variable>
<Add>
<addName>
<xsl:value-of select="$TownName"/>
</addName>
<addType>
<xsl:value-of select="$TownType"/>
</addType>
</Add>
</xsl:template>
</xsl:stylesheet>
In the second xsl Address2.xsl I want to import the value of addName.Tried below xsl but not getting the value.
Address2.xsl
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0">
<xsl:import href=" Address1.xsl "/>
<xsl:template match="/ ">
<xsl:variable name="TempVar">
<xsl:copy>
<xsl:call-template name="Addtemplate">
<xsl:value-of select=' addName'/>**<!-- I want addName value in the Address1.xsl to be mapped to TempVar variable -->**
</xsl:call-template>
</xsl:copy>
</xsl:variable>
</xsl:template>
</xsl:stylesheet>

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.

Select a parallel node in xslt

Consider following xml:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<base>
<a>
<b>
<c>Text 1</c>
</b>
</a>
</base>
<base>
<a>
<b>
<c>Text 2</c>
</b>
</a>
</base>
</root>
and this xsl
<?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="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="root/base[1]" />
</xsl:template>
<xsl:template match="base//*">
<xsl:if test="text()">
<xsl:value-of select="text()" />
<!-- i need Text 2 here -->
</xsl:if>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="text()" />
</xsl:stylesheet>
The original xml is much more nested an i don't know the exact structure. But there is a parallel node with the same structure. If my template is at //root/base[1]/a/b/c I want to reference //root/base[2]/a/b/c
However I only know that I am in some node below //root/base[1] and that there is the same node in //root/base[2].
Is there a possibility to accomplish this goal?
This stylesheet requires no extensions, and does not make any assumption on unique names; the boring part is to pass the correct "twin node" every time you call xsl:apply-templates:
<?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="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="root/base[1]">
<xsl:with-param name="twin" select="root/base[2]"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="*">
<xsl:param name="twin"/>
<xsl:if test="text()">
<xsl:value-of select="text()" />
<xsl:value-of select="$twin/text()"/>
</xsl:if>
<xsl:for-each select="*">
<xsl:variable name="pos" select="position()"/>
<xsl:apply-templates select=".">
<xsl:with-param name="twin" select="$twin/*[$pos]"/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:template>
<xsl:template match="text()" />
</xsl:stylesheet>
Solution with using saxon:evaluate extension (http://saxon.sourceforge.net/saxon7.9/extensions.html#evaluate)
You can also use dyn:evaluate if you are using xslt 1.0 processors (http://exslt.org/dyn/index.html):
<?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"
xmlns:saxon="http://saxon.sf.net/"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="root/base[1]" />
</xsl:template>
<xsl:template match="base//*">
<xsl:if test="text()">
<xsl:variable name="path">
<xsl:call-template name="constructPath">
<xsl:with-param name="node" select="."/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="rewritedPath" select="concat('/root/base[2]', substring($path, 11))"/>
<xsl:value-of select="text()" />
<xsl:value-of select="saxon:evaluate($rewritedPath)" />
</xsl:if>
<xsl:apply-templates />
</xsl:template>
<xsl:template name="constructPath">
<xsl:param name="node"/>
<xsl:choose>
<xsl:when test="$node/parent::node()/name()">
<xsl:call-template name="constructPath">
<xsl:with-param name="node" select="$node/parent::node()"/>
</xsl:call-template>
<xsl:value-of select="concat('/', $node/name())"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('/', $node/name())"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="text()" />
</xsl:stylesheet>
Here we use recursive template "constructPath" to create string and process it in evaluate() function. This will select the exact node as in the parallel branch. Hope this will help.
Try the code below. This code will work only if each base element has children with unique element name.
<?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="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="root/base[1]" />
</xsl:template>
<xsl:template match="base//*">
<xsl:if test="text()">
<xsl:variable name="name" select="name()"/>
<xsl:value-of select="text()" />
<xsl:value-of select="ancestor::base/following-sibling::base[1]//*[name() = $name]" />
</xsl:if>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="text()" />
However you can use dyn:evaluate from exslt extension or saxon:eval

How can I summarize nodes using calculation data from other nodes

Using XSLT 1.0, how can I summarize subnodes under a given node while modifiyng the content with data from another set of nodes in an elegant way? Assume I have this xml:
<Root>
<ExchangeRates>
<ExchangeRate>
<CurrencyCode>USD</CurrencyCode>
<Rate>6.4</Rate>
</ExchangeRate>
<ExchangeRate>
<CurrencyCode>EUR</CurrencyCode>
<Rate>8.44</Rate>
</ExchangeRate>
<ExchangeRate>
<CurrencyCode>SEK</CurrencyCode>
<Rate>1</Rate>
</ExchangeRate>
</ExchangeRates>
<Prices>
<Price>
<Currency>SEK</Currency>
<Amount>10000</Amount>
</Price>
<Price>
<Currency>EUR</Currency>
<Amount>1000</Amount>
</Price>
<Price>
<Currency>USD</Currency>
<Amount>1000</Amount>
</Price>
</Prices>
</Root>
I want the sum of all Amounts converted into SEK with the help of the ExchangeRates. The result should be:
<SumInSEK>24840</SumInSEK>
If I didn't have to convert the amounts I would simply use the xpath sum() function. Is is possible to use that function in this case?
Another possible solution without recursive calls but with exsl extension.
This make use of the key definition form #softwarebear.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:key name="rates" match="//ExchangeRate/Rate" use="parent::*/child::CurrencyCode/text()"/>
<xsl:template match="/" >
<xsl:apply-templates select="//Prices"/>
</xsl:template>
<xsl:template match="Prices">
<SUmInSEK>
<xsl:variable name="price_SEK">
<xsl:apply-templates mode="SEK" />
</xsl:variable>
<xsl:value-of select="sum(exsl:node-set($price_SEK)//price_SEK)"/>
</SUmInSEK>
</xsl:template>
<xsl:template match="Price" mode="SEK">
<price_SEK>
<xsl:value-of
select="number(Amount) * number( key('rates', Currency) )" />
</price_SEK>
</xsl:template>
</xsl:stylesheet>
Wiht the output
<SUmInSEK>24840</SUmInSEK>
I thought this might be simple ... but I don't think you can use sum() on this occasion ... the best I can do is a recursive template.
?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:key name="rates" match="//ExchangeRate/Rate" use="parent::*/child::CurrencyCode/text()"/>
<xsl:template match="//Prices">
<SUmInSEK>
<xsl:call-template name="sum"/>
</SUmInSEK>
</xsl:template>
<xsl:template name="sum">
<xsl:param name="iterator" select="1"/>
<xsl:param name="total" select="0"/>
<xsl:variable name="price" select="child::Price[$iterator]"/>
<xsl:variable name="current">
<xsl:value-of select="number($price/child::Amount) * number( key('rates', $price/child::Currency) )"/>
</xsl:variable>
<xsl:variable name="newtotal">
<xsl:value-of select="$total + $current"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$price/following-sibling::Price">
<xsl:call-template name="sum">
<xsl:with-param name="iterator" select="$iterator+1"/>
<xsl:with-param name="total" select="$newtotal"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$total + $current"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="* | /">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="text() | #*">
</xsl:template>
<xsl:template match="processing-instruction() | comment()" />
</xsl:stylesheet>