Applying consecutive transformations in xslt - xslt

I am a newbie to xslt and I have a variable "name" which stores a result of a transformation how can we transform the variable "name" using some other template in same xslt file.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="#* | node()" >
<xsl:variable name="name">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:variable>
</xsl:template>
<xsl:template match="ns1:BP7Locations" >
<xsl:copy>
<xsl:apply-templates select="ns1:Entry">
<xsl:sort select="ns4:Location/ns4:LocationNum" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

In XSLT 2.0 the typical pattern for a multi-phase transformation is
<xsl:variable name="temp1">
<xsl:apply-templates mode="phase1"/>
</xsl:variable>
<xsl:variable name="temp2">
<xsl:apply-templates select="$temp1" mode="phase2"/>
</xsl:variable>
<xsl:apply-templates select="$temp2" mode="phase3"/>
In XSLT 1.0 this isn't allowed, because the variable holds a "result tree fragment" which can only be processed in very limited ways. Nearly every XSLT 1.0 processor implements the exslt:node-set() extension function so you can get around this restriction. The code then becomes:
<xsl:variable name="temp1">
<xsl:apply-templates mode="phase1"/>
</xsl:variable>
<xsl:variable name="temp2">
<xsl:apply-templates select="exslt:node-set($temp1)" mode="phase2"/>
</xsl:variable>
<xsl:apply-templates select="exslt:node-set($temp2)" mode="phase3"/>
You will need to add the namespace xmlns:exslt="http://exslt.org/common" to your stylesheet.
You don't have to use different modes for the different phases of processing, but it helps to avoid hard-to-spot bugs: the template rules for each processing phase should have corresponding mode attributes, and it can also be a good idea to put the rules for each mode in a separate stylesheet module.

For an example, you can consider this XML:
<root>
<a>value</a>
</root>
And this XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="root">
<xsl:variable name="a">
<xsl:apply-templates select="a" mode="mode1"/>
</xsl:variable>
<xsl:apply-templates select="exslt:node-set($a)/a" mode="mode2"/>
</xsl:template>
<xsl:template match="a" mode="mode1">
<a><xsl:value-of select="'mode1 called'"/></a>
</xsl:template>
<xsl:template match="a" mode="mode2">
<a><xsl:value-of select="concat(., ' mode2 called')"/></a>
</xsl:template>
</xsl:stylesheet>
This produces the following as output:
<?xml version="1.0" encoding="utf-8"?>
<a xmlns:exslt="http://exslt.org/common">mode1 called mode2 called</a>
The XSLT's first template has a variable a which stores the data after processing element <a> and then the xsl:apply-templates processes the data in the variable a again. Here the #mode on xsl:template differentiates the second and the third templates.

Related

XSLT 1.0, copy all child nodes except some

I am trying to copy all child nodes to a specific node, except a few. Haven't been able to get this to work? Any pointers of what I am doing wrong?
Using this XML:
<ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/">
<ns0:Header>
<wsse:Sec xmlns:wsse="http://docs.x.org/wsse/">
<saml:Ass xmlns:saml="http://docs.x.org/saml/">
<ds:Sign xmlns:ds="http://docs.x.org/ds/">
<ds:SignVal>SignatureValue</ds:SignVal>
</ds:Sign>
<saml:subj>SubjectValue</saml:subj>
</saml:Ass>
</wsse:Sec>
<To>http://localhost:8080/Test/</To>
<Action>SendTest</Action>
</ns0:Header>
<ns0:Body>...</ns0:Body>
</ns0:Envelope>
The wanted result is to just get the Sec tag and all children:
<wsse:Sec xmlns:wsse="http://docs.x.org/wsse/">
<saml:Ass xmlns:saml="http://docs.x.org/saml/">
<ds:Sign xmlns:ds="http://docs.x.org/ds/">
<ds:SignVal>SignatureValue</ds:SignVal>
</ds:Sign>
<saml:subj>SubjectValue</saml:subj>
</saml:Ass>
</wsse:Sec>
I have tried numerous XSL including this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:template match="Header">
<xsl:copy-of select="*"/>
</xsl:template>
<!-- Exclude these -->
<xsl:template match="To" />
<xsl:template match="Action" />
</xsl:stylesheet>
The result is I get values but no tags...
You have not accounted for namespaces in your XSLT. In your XML, Header is in namespace http://schemas.xmlsoap.org/soap/envelope/, but your XSLT is trying to match a Header in no namespace.
You need to declare the namespaces in your XSLT, and use them in the template matches
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsse="http://docs.x.org/wsse/">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:template match="ns0:Header">
<xsl:copy-of select="wsse:Sec"/>
</xsl:template>
<xsl:template match="ns0:Body" />
</xsl:stylesheet>
Note this XSLT doesn't need templates matching "To" and "Action" because of the explicit copy of wsse:Sec using this approach. However, you do need to template to ensure any test within ns0:Body isn't picked up.
Another approach is to use the identity template, and then you would have the templates to exclude To and Action (and Body)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsse="http://docs.x.org/wsse/">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ns0:Envelope|ns0:Header">
<xsl:apply-templates />
</xsl:template>
<!-- Exclude these -->
<xsl:template match="ns0:Body|To|Action" />
</xsl:stylesheet>
Note there is a template matching ns0:Envelope and ns0:Header as although you don't want these elements themselves, you do need to process the child nodes.
You would need to use XSLT 2 or 3 with
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:wsse="http://docs.x.org/wsse/"
exclude-result-prefixes="#all"
version="3.0">
<xsl:template match="/">
<xsl:copy-of select="//wsse:Sec" copy-namespaces="no"/>
</xsl:template>
</xsl:stylesheet>
to get the posted result with a simple copy instruction: https://xsltfiddle.liberty-development.net/bnnZVw
In XSLT 1 the copy will always copy the in-scope namespace xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" so to remove it from the result you would need to run your code through some kind of transformation stripping in-scope namespaces (other than the one of the element itself):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wsse="http://docs.x.org/wsse/"
exclude-result-prefixes="wsse"
version="1.0">
<xsl:template match="#*">
<xsl:attribute name="{name()}" namespace="{namespace-uri()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="//wsse:Sec"/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/bnnZVw/1

xsl:strip-space combined with xsl:text messes up automatic indentation

Using XSLT 1.0 I would like to comment out certain XML elements and replace other XML elements, while keeping the XML nicely formatted.
For example, the following XML document
<doc>
<e1>foo</e1>
<e2>bar</e2>
</doc>
should be converted to
<doc>
<!--<e1>foo</e1>-->
<e3>foobar</e3>
<e4>foobar</e4>
</doc>
I am using the following XSL transformation and xsltproc for testing it:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*" />
<xsl:output indent="yes" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="e1">
<xsl:text disable-output-escaping="yes"><!--</xsl:text> <!--*-->
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
<xsl:text disable-output-escaping="yes">--></xsl:text> <!--*-->
</xsl:template>
<xsl:template match="e2">
<e3>foobar</e3><e4>foobar</e4>
</xsl:template>
</xsl:stylesheet>
But what I get is this:
<doc><!--<e1>foo</e1>--><e3>foobar</e3><e4>foobar</e4></doc>
The problem seems to be caused by the lines marked with '*' in my transformation; more specifically from inserting <!-- and -->. When I remove these two elements, the result is indented as expected.
Is there a way to wrap elements in comments while still keeping the output document nicely formatted?
Try whether outputting a comment with the serialization of the element, as in
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:import href="http://lenzconsulting.com/xml-to-string/xml-to-string.xsl"/>
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="e1">
<xsl:comment>
<xsl:call-template name="xml-to-string"></xsl:call-template>
</xsl:comment>
</xsl:template>
<xsl:template match="e2">
<e3>foobar</e3><e4>foobar</e4>
</xsl:template>
</xsl:stylesheet>
gives you a better result.

Change the value of a tag only if another tag has an specific value

I'm using this xsl to change two tags of some xml
xsl
xsltproc - "filename" << EOF
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="no"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root/attr1/text()">
<xsl:text>new-text</xsl:text>
</xsl:template>
<xsl:template match="root/group1/attr1/text()">
<xsl:text>another-new-text</xsl:text>
</xsl:template>
</xsl:stylesheet>
EOF
xml
<root>
<attr1>someold</attr1>
<group1>
<attr1>anotherold</attr1>
</group1>
<attr2>0</attr2>
</root>
output
<root>
<attr1>new-text</attr1>
<group1>
<attr1>another-new-text</attr1>
</group1>
<attr2>0</attr2>
</root>
This xsl works great for my needs but now I need to validate attr2 before the transformation. If attr2 is 0 I need to change, otherwise I should leave the old value.
I have hundreds of xml to convert, each one with hundreds of lines, because of this I'm looking for an automatic way to validate. I tried xsl:if but couldn't figure out where to place the tag and how to build the test attribute.
How to change the value of a tag only if another tag has an specific value? Other improvements on the xsl are also welcome.
You can add conditions in match patterns, e.g. <xsl:template match="root[attr2 = 0]/attr1/text()">...</xsl:match> and/or <xsl:template match="root[attr2 = 0]/group1/attr1/text()">.
you can take the attr2 as variable and use the variable to validate your conditions.....
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="no"/>
<xsl:variable name="attr2" select="root/attr2 "/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root/attr1/text()">
<xsl:value-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:text>new-text</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="root/group1/attr1/text()">
<xsl:text>another-new-text</xsl:text>
</xsl:template>

apply two consecutive xslt transformations

I have two xslt transformations to apply to an xml message.
The first is to drop all namespaces and prefixes. here is the code :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()[not(self::*)]">
<xsl:copy/>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The second one is to select elements from the output of the first :
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<onSale><xsl:value-of select="//entry/content/product_product/sale_ok" /></onSale>
<onlineOnly><xsl:value-of select="//entry/content/product_product/online" /></onlineOnly>
<name><xsl:value-of select="//entry/content/product_product/name" /></name>
<isbn><xsl:value-of select="//entry/content/product_product/isbn" /></isbn>
<price><xsl:value-of select="//entry/content/product_product/price" /></price>
<active><xsl:value-of select="//entry/content/product_product/active" /></active>
<format><xsl:value-of select="//entry/content/product_product/format" /></format>
<collection><xsl:value-of select="//entry/content/product_product/collection" /></collection>
<dateParution><xsl:value-of select="//entry/content/product_product/date_parution"/></dateParution>
<ean13><xsl:value-of select="//entry/content/product_product/ean13"/></ean13>
</xsl:template>
</xsl:stylesheet>
How I can apply the two of them in one xslt transformation without doing two transformation separately .
Thanks
I don't know which version of XSLT is supported in your environment, with XSLT 2.0 you can use
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="step1">
<xsl:apply-templates mode="step1"/>
</xsl:variable>
<xsl:template match="#*|node()[not(self::*)]" mode="step1">
<xsl:copy/>
</xsl:template>
<xsl:template match="*" mode="step1">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="node()|#*" mode="step1"/>
</xsl:element>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="$step1//entry"/>
</xsl:template>
<xsl:template match="entry">
<onSale><xsl:value-of select="content/product_product/sale_ok" /></onSale>
<onlineOnly><xsl:value-of select="content/product_product/online" /></onlineOnly>
<name><xsl:value-of select="content/product_product/name" /></name>
<isbn><xsl:value-of select="content/product_product/isbn" /></isbn>
<price><xsl:value-of select="content/product_product/price" /></price>
<active><xsl:value-of select="content/product_product/active" /></active>
<format><xsl:value-of select="content/product_product/format" /></format>
<collection><xsl:value-of select="content/product_product/collection" /></collection>
<dateParution><xsl:value-of select="content/product_product/date_parution"/></dateParution>
<ean13><xsl:value-of select="content/product_product/ean13"/></ean13>
</xsl:template>
</xsl:stylesheet>
I would further refactor however and write templates like
<xsl:template match="sale_ok">
<onSale>
<xsl:value-of select="."/>
</onSale>
</xsl:template>
If you use XSLT 1.0 you need an extension function to process the contents of a variable with apply-templates so doing
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="step1">
<xsl:apply-templates mode="step1"/>
</xsl:variable>
<xsl:template match="#*|node()[not(self::*)]" mode="step1">
<xsl:copy/>
</xsl:template>
<xsl:template match="*" mode="step1">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="node()|#*" mode="step1"/>
</xsl:element>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="exsl:node-set($step1)//entry"/>
</xsl:template>
<xsl:template match="entry">
<onSale><xsl:value-of select="content/product_product/sale_ok" /></onSale>
<onlineOnly><xsl:value-of select="content/product_product/online" /></onlineOnly>
<name><xsl:value-of select="content/product_product/name" /></name>
<isbn><xsl:value-of select="content/product_product/isbn" /></isbn>
<price><xsl:value-of select="content/product_product/price" /></price>
<active><xsl:value-of select="content/product_product/active" /></active>
<format><xsl:value-of select="content/product_product/format" /></format>
<collection><xsl:value-of select="content/product_product/collection" /></collection>
<dateParution><xsl:value-of select="content/product_product/date_parution"/></dateParution>
<ean13><xsl:value-of select="content/product_product/ean13"/></ean13>
</xsl:template>
</xsl:stylesheet>
On the other I wonder why we are getting lots of questions recently trying to eliminate namespaces or to ignore them, if you really want to do that with XSLT 2.0 then doing
<xsl:template match="*:sale_ok">
<onSale>
<xsl:value-of select="."/>
<onSale>
</xsl:template>
and so on suffices.

Apply XSLT Transform to an already transformed XML

All,
I have an XML file which I transform it using an XSLT document to another XML.
Can I define another set of transformations in the same XSLT file to be applied in the result XML of the first transformation?
Thanks,
MK
Yes.
I. This XSLT 1.0 transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vrtfPass1">
<xsl:apply-templates/>
</xsl:variable>
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()|#*" mode="pass2">
<xsl:copy>
<xsl:apply-templates select="node()|#*" mode="pass2"/>
</xsl:copy>
</xsl:template>
<xsl:template match="num/text()">
<xsl:value-of select="2*."/>
</xsl:template>
<xsl:template match="num/text()" mode="pass2">
<xsl:value-of select="1+."/>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="ext:node-set($vrtfPass1)/*" mode="pass2"/>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<t>
<num>1</num>
<num>2</num>
<num>3</num>
<num>4</num>
<num>5</num>
</t>
produces:
<t>
<num>3</num>
<num>5</num>
<num>7</num>
<num>9</num>
<num>11</num>
</t>
Do note:
Two transformations are actually performed, the second is performed on the result of the first.
The result of the first transformation is the content of the variable $vrtfPass1.
In XSLT 1.0 the type of variables that contain dynamically generated (temporary) XML trees (XML document or XML fragment) is RTF (Result-Tree-Fragment). No XPath operations are possible on an RTF -- it needs to be converted to a regular node-set using the extension function xxx:node-set(), which is provided by the vast majority of XSLT 1.0 processor vendors. In this example exslt:node-set() is used, because EXSLT is implemented by many different vendors.
The second transformation is applied on the result of the first: <xsl:apply-templates select="ext:node-set($vrtfPass1)/*" mode="pass2"/> . A separate mode is used in order to cleanly separate the code of the two transformations.
The first transformation multiplies each num/text() by 2. The second transformation increments each num/text(). The result is 2*.+1
II. This XSLT 2.0 transformation:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" >
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vPass1">
<xsl:apply-templates mode="pass1"/>
</xsl:variable>
<xsl:template match="node()|#*" mode="pass1">
<xsl:copy>
<xsl:apply-templates select="node()|#*" mode="pass1"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()|#*" mode="pass2">
<xsl:copy>
<xsl:apply-templates select="node()|#*" mode="pass2"/>
</xsl:copy>
</xsl:template>
<xsl:template match="num/text()" mode="pass1">
<xsl:value-of select="2*xs:integer(.)"/>
</xsl:template>
<xsl:template match="num/text()" mode="pass2">
<xsl:value-of select="1+."/>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="$vPass1" mode="pass2"/>
</xsl:template>
</xsl:stylesheet>
when applied on the same XML document, produces the same wanted and correct result.
Do note: In XSLT 2.0/XPath 2.0 the RTF type has been abolished. No xxx:node-set() extension function is needed.