How to wrap input xsml in c data using xslt - xslt

input to the xslt will be like below:
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
Output of the xsl should be like below:
<Output>
<![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
]]>
</Output>
I have written below code but < and > are not replacing with < > in the output.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="output">
<output>
<xsl:text disable-output-escaping="yes"><![CDATA[</xsl:text>
<xsl:text disable-output-escaping="yes"> <?xml version="1.0" encoding="UTF-8"?> </xsl:text>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<xsl:text disable-output-escaping="yes">]]></xsl:text>
</output>
</xsl:variable>
<xsl:copy-of select="$output"/>
</xsl:template>
</xsl:stylesheet>

Need to create CDATA out of variable like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="/">
<xsl:variable name="output">
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
</xsl:variable>
<output>
<xsl:text disable-output-escaping="yes"><![CDATA[</xsl:text>
<xsl:text disable-output-escaping="yes">
</xsl:text>
<xsl:text disable-output-escaping="yes"><?xml version="1.0" encoding="UTF-8"?></xsl:text>
<xsl:copy-of select="$output"/>
<xsl:text disable-output-escaping="yes">
]]></xsl:text>
</output>
</xsl:template>
</xsl:stylesheet>

CDATA sections are really just an alternate escaping mechanism: at an XML level your desired result is the <Output/> element with text content that happens to be the serialized output. DataPower includes a <dp:serialize/> extension that can do this.
I think a working stylesheet should look something like
<?xml version="1.0"?>
<xsl:stylesheet
version="1.0"
extension-element-prefixes="dp"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dp="http://www.datapower.com/extensions">
<xsl:output method="xml" cdata-section-elements="Output"/>
<xsl:template match="/">
<Output>
<dp:serialize select="."/>
</Output>
</xsl:template>
</xsl:stylesheet>
(This has always been a little bit odd construction, because you can embed XML in XML directly; if you control the application and schema you might consider trying to change it so you don't need to XML parse the text content of an XML element.)

Related

How to select the preceding-sibling inside a for each loop?

My XML
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<cd>
<title>
<name>Empire Burlesque1 </name>
</title>
</cd>
<cd>
<title>
<name>Empire Burlesque 2</name>
</title>
</cd>
<cd>
<title>
<name>Empire Burlesque 2</name>
</title>
</cd>
</catalog>
XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="catalog/cd">
<xsl:call-template name="currentValue" />
<xsl:call-template name="prevValue" />
</xsl:for-each>
</xsl:template>
<xsl:template name="currentValue">
<xsl:value-of select="title/name" />
</xsl:template>
<xsl:template name="prevValue">
<xsl:value-of select="preceding-sibling::title[name][1]" />
</xsl:template>
</xsl:stylesheet>
The preceding sibling does not print anything. I want to store both in different variables and compare them. Can you help me pointing what is wrong here?
Currently, when you are checking for the preceding element you are positioned on cd element, and that only has other cd elements as siblings. Therefore, the expression you want is this:
<xsl:value-of select="preceding-sibling::cd[1]/title/name" />

XSLT sum concatenated instead of summed

I have the following input
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<cd>
<carac NAME="aaa" NOT="10"/>
<value VAL="1"/>
</cd>
<cd>
<carac NAME="aaa" NOT="10"/>
<value VAL="2"/>
</cd>
<cd>
<carac NAME="aaa" NOT="20"/>
<value VAL="3"/>
</cd>
<cd>
<carac NAME="aaa" NOT="10"/>
<value VAL="4"/>
</cd>
<cd>
<carac NAME="bbb" NOT="30"/>
<value VAL="5"/>
</cd>
<cd>
<carac NAME="bbb" NOT="30"/>
<value VAL="6"/>
</cd>
<cd>
<carac NAME="ccc" NOT="40"/>
<value VAL="7"/>
</cd>
<cd>
<carac NAME="ccc" NOT="50"/>
<value VAL="8"/>
</cd>
</catalog>
and I want to get for every different NAME the sum of all the different NOT, so if for the same NAME the NOT is repeated, it has to be summed only once.
The output for this example has to be: aaa30 bbb30 ccc90
My XSL looks like this, but instead of giving the result I want is showing aaa1020 bbb30 ccc4050
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="nameList" match="catalog/cd/carac" use="#NAME"/>
<xsl:key name="notList" match="catalog/cd/carac" use="concat(#NAME,'_',#NOT)"/>
<xsl:template match="/">
<html>
<body>
<xsl:for-each select="//carac[generate-id()=generate-id(key('nameList', #NAME)[1])]">
<xsl:variable name="name" select="./#NAME"/>
<xsl:variable name="lines">
<xsl:for-each select="//carac[generate-id()=generate-id(key('notList',concat($name,'_',#NOT))[1])]">
<noti>
<xsl:value-of select="#NOT"/>
</noti>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="$name"/>
<xsl:value-of select="sum($lines)"/>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Try it this way?
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="nameList" match="catalog/cd/carac" use="#NAME"/>
<xsl:key name="notList" match="catalog/cd/carac" use="concat(#NAME, '_', #NOT)"/>
<xsl:template match="/catalog">
<html>
<body>
<xsl:for-each select="cd/carac[generate-id()=generate-id(key('nameList', #NAME)[1])]">
<xsl:value-of select="#NAME"/>
<xsl:variable name="current-group" select="key('nameList', #NAME)" />
<xsl:value-of select="sum($current-group[generate-id()=generate-id(key('notList', concat(#NAME, '_', #NOT))[1])]/#NOT)"/>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

xslt copy-of to different namespace in the resulting document

In my xslt code, I can use copy-of to copy the element and its descendant nodes, but the catch is that the resulting schema though has the same structure has a different namespace. Is there any way I can still use copy-of and accomplish this? I am using XSLT 2.0
Following is the example of Source and Target XMLs, the cd elements can be copied using copy-of in XSL, but they have different namespaces.
Source XML
<catalog xmlns="namespace1">
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
</catalog>
Target XML
<books xmlns="namespace2">
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
</books>
Used Martin Honnen's idea and created this
<xsl:template name="changeNamespace">
<xsl:param name="nodes"/>
<xsl:for-each select="$nodes">
<xsl:choose>
<xsl:when test="count(child::*)>0">
<xsl:element name="newnamespace:{local-name()}">
<xsl:call-template name="changeNamespace">
<xsl:with-param name="nodes" select="./child::*"/>
</xsl:call-template>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:element name="newnamespace:{local-name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
You can't use xsl:copy or xsl:copy-of to change the namespace of a node, you need to transform the nodes with e.g.
<xsl:template match="ns1:*">
<xsl:element name="ns2:{local-name()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
which then assumes you have e.g. <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns1="source-namespace" xmlns:ns2="target-namespace" version="1.0"> in scope or you can of course also put the target namespace into the xsl:element:
<xsl:template match="ns1:*">
<xsl:element name="ns2:{local-name()}" namespace="target-namespace">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
If you have attributes where you need to change the namespace you need to set up a similar template for attribute nodes.
With XSLT 2.0 you can simplify the stylesheet structure with e.g.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="namespace1" xmlns="namespace2" version="2.0">
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>

Retrieve value of 5th to 10th titles in a collection of 15-16 titles using XSLT

Below is my XML file and I would like to retrieve titles 3 to 4 of the XML file using some form of count function with XSLT. Please help... thanks for your help
<?xml version="1.0">
<catalog>
<cd>
<title>Empire Burlesque</title>
</cd>
<cd>
<title>Hide your heart</title>
</cd>
<cd>
<title>Greatest Hits</title>
</cd>
<cd>
<title>Still got the blues</title>
</cd>
</catalog>
Try this:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<result>
<cd><xsl:value-of select="catalog/cd[3]/title"/></cd>
<cd><xsl:value-of select="catalog/cd[4]/title"/></cd>
</result>
</xsl:template>
</xsl:stylesheet>
You are looking for the position() XPath function.
For example:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<result>
<xsl:copy-of
select="catalog/cd[position() >= 3 and position() <= 4]/title"/>
</result>
</xsl:template>
</xsl:stylesheet>
This short and completely "push style" transformation:
<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="cd/node()"/>
<xsl:template match="cd[position() >= 3 and 4 >= position()]/title">
<xsl:copy><xsl:apply-templates/></xsl:copy>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<catalog>
<cd>
<title>Empire Burlesque</title>
</cd>
<cd>
<title>Hide your heart</title>
</cd>
<cd>
<title>Greatest Hits</title>
</cd>
<cd>
<title>Still got the blues</title>
</cd>
</catalog>
produces the wanted, correct result:
<title>Greatest Hits</title>
<title>Still got the blues</title>
Explanation:
The empty-bodied template <xsl:template match="cd/node()"/> prevents the processing ("deletes") of any child of a cd .
The second template overrides the first only for a title child of a cd whose position() is not less than 3 and not greater than 4. It effectively copies the matched title element.
The <xsl:strip-space elements="*"/> instruction makes all this possible by deleting from the XML document all white-space-only text nodes. In this way the positions of cd elements in the node-list formed by the <xsl:apply-templates> instruction (in the built-in XSLT template for elements) would be 1, 2, 3, 4 and not 2, 4, 6, 8.

How to merge two xml file using xslt1.0?

File1.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<catalog>
<data>
<title>Title1</title>
<description>Description1</description>
<myid>1</myid>
</data>
<data>
<title>Title2</title>
<description>Description2</description>
<myid>2</myid>
</data>
</catalog>
File2.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<catalog>
<data>
<author>Author1</author>
<date>12/34/5678</date>
<myid>1</myid>
</data>
<data>
<author>Author2</author>
<date>87/65/4321</date>
<myid>2</myid>
</data>
</catalog>
need output like below using xslt1.0
<catalog>
<data>
<title>Title1</title>
<description>Description1</description>
<myid>1</myid>
<author>Author1</author>
<date>12/34/5678</date>
</data>
<data>
<title>Title2</title>
<description>Description2</description>
<myid>2</myid>
<author>Author2</author>
<date>87/65/4321</date>
</data>
</catalog>
You need to use the document() function, like so:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>
<xsl:template match="/">
<catalog>
<!-- Apply to all data elements in file 1 -->
<xsl:apply-templates select="document('file1.xml')/catalog/data" />
</catalog>
</xsl:template>
<xsl:template match="data">
<data>
<!--Use myid as a lookup-->
<xsl:variable name="myId" select="myid/text()" />
<!--copy all data child nodes from file1-->
<xsl:copy-of select="#* | node()"/>
<!--copy all data child nodes from file2, excluding myid as we already have it-->
<xsl:copy-of select="document('file2.xml')/catalog/data[myid=$myId]/*[not(local-name()='myid')]"/>
</data>
</xsl:template>
</xsl:stylesheet>