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.
Related
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.
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>
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.
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.
I have a style-sheet that I am using to remove certain elements based on the value of an other element. However, it is not working ...
Sample Input XML
<Model>
<Year>1999</Year>
<Operation>ABC</Operation>
<Text>Testing</Text>
<Status>Ok</Status>
</Model>
If Operation value is 'ABC' then remove Text and Status nodes from XML.
And gives the following output.
<Model>
<Year>1999</Year>
<Operation>ABC</Operation>
</Model>
Here is my style sheet that I am using but it is removing Text and Status nodes from all XMLs even when operation is not 'ABC'.
<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:variable name="ID" select="//Operation"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Text | Status">
<xsl:if test ="$ID ='ABC'">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Thanks in Advance
How would I do the same when namespace is present like
<ns0:next type="Sale" xmlns:ns0="http://Test.Schemas.Inside_Sales">
Here is a complete XSLT transformation -- short and simple (no variables, no xsl:if, xsl:choose, xsl:when, xsl:otherwise):
<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()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"*[Operation='ABC']/Text | *[Operation='ABC']/Status"/>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<Model>
<Year>1999</Year>
<Operation>ABC</Operation>
<Text>Testing</Text>
<Status>Ok</Status>
</Model>
the wanted, correct result is produced:
<Model>
<Year>1999</Year>
<Operation>ABC</Operation>
</Model>
Change your xsl:if as follows:
<xsl:if test="../Operation!='ABC'">
and you can get rid of xsl:variable.
A better pattern in XSLT than using <xsl:if> is to add new templates with match conditions:
<xsl:template match="(Text | Status)[../Operation != 'ABC']"/>
I found this works:
<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()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/Model">
<xsl:choose>
<xsl:when test="Operation[text()!='ABC']">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="Year"/>
<xsl:apply-templates select="Operation"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>