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>
Related
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>
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.)
I have a certain requirement where, I need to move the sequence element values to another newly created element according to the the number of values in the original sequence.
Please find my Input XML and the Desired Output XML .
help is highly appreciated
Rule:
Move the value of Addr1 (catalogue/cd11/Location/Addr/Addr1) to
catalogue/cd11/Location/primary/original/Address1/place. primary/original/Address1/place need to be created.
Input XML:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://www.altova.com">
<publisher>
<Name id="d123">
<Place>Chicago</Place>
</Name
<catalogue id="d1" >
<cd11 id="d2">
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<year>1985</year>
<Location id="d1234">
<Addr id="d234">
<Addr1 id="d565">catherine Av</Addr1>
<Addr2 id="d566">block a</Addr2>
<City id="d567">chicago</City>
</Addr>
<Addr id="d334">
<Addr1 id="d665">Illinois st</Addr1>
<Addr2 id="d666">block a</Addr2>
<City id="d667">chicago</City>
</Addr>
</Location>
</cd11>
</catalogue>
<catalogue id="d3" >
<cd11 id="d4">
<title>Jurassic World</title>
<artist>Chris Pratt</artist>
</cd11>
</catalogue>
</publisher>
</root>
Output XML:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://www.example.com">
<publisher>
<Name id="d123">
<Place>Chicago</Place>
</Name>
<catalogue id="d1">
<cd11 id="d2">
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<year>1985</year>
<Location id="d1234">
<Addr id="d234">
<Addr1 id="d565">catherine Av</Addr1>
<Addr2 id="d566">block a</Addr2>
<City id="d567">chicago</City>
</Addr>
<Addr id="d334">
<Addr1 id="d665">Illinois st</Addr1>
<Addr2 id="d666">block a</Addr2>
<City id="d667">chicago</City>
</Addr>
<primary>
<original>
<test>test value</test>
<Address1>
<place>catherine Av</place>
</Address1>
<Address1>
<place>Illinois st</place>
</Address1>
</original>
</primary>
</Location>
</cd11>
</catalogue>
<catalogue id="d3">
<cd11 id="d4">
<title>Jurassic World</title>
<artist>Chris Pratt</artist>
</cd11>
</catalogue>
</publisher>
</root>
Thanks in advance.
You can write a template for Location elements that inserts the new elements and transforms the Addr1 elements:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xpath-default-namespace="http://www.altova.com" xmlns="http://www.altova.com">
<xsl:output indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="catalogue/cd11/Location">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
<primary>
<original>
<Address1>
<xsl:apply-templates select="Addr/Addr1" mode="convert"/>
</Address1>
</original>
</primary>
</xsl:copy>
</xsl:template>
<xsl:template match="Addr/Addr1" mode="convert">
<place>
<xsl:value-of select="."/>
</place>
</xsl:template>
</xsl:transform>
Online sample at http://xsltransform.net/ncdD7mv.
According to your comment and edit you do not want to copy the elements, instead you want to transform them to a new namespace, so you need to change all uses of xsl:copy of an element to create an element of the same local name but with the new namespace (which will simply work if you have the right xmlns="http://www.example.com" in the XSLT and use xsl:element name="{local-name()}"):
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xpath-default-namespace="http://www.altova.com" xmlns="http://www.example.com">
<xsl:output indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="catalogue/cd11/Location">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#* | node()"/>
<primary>
<original>
<Address1>
<xsl:apply-templates select="Addr/Addr1" mode="convert"/>
</Address1>
</original>
</primary>
</xsl:element>
</xsl:template>
<xsl:template match="Addr/Addr1" mode="convert">
<place>
<xsl:value-of select="."/>
</place>
</xsl:template>
</xsl:transform>
Can someone help me with the below xslt question.
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<cd>
<title rowmerge="f">Title 1</title>
<artist rowmerge="f">sample 1</artist>
<price rowmerge="T">1</price>
<year >1985</year>
</cd>
<cd>
<title rowmerge="F">Title 2</title>
<artist rowmerge="F">Sample 2</artist>
<price rowmerge="T"></price>
<year>1988</year>
</cd>
<cd>
<title rowmerge="F">Title 3</title>
<artist rowmerge="F">Sample 3</artist>
<price rowmerge="F">3</price>
<year>1988</year>
</cd>
<cd>
<title rowmerge="T">Title 4</title>
<artist rowmerge="F">sample 4</artist>
<price rowmerge="T">4</price>
<year >1985</year>
</cd>
<cd>
<title rowmerge="T"></title>
<artist rowmerge="F">Sample 5</artist>
<price rowmerge="T"></price>
<year>1988</year>
</cd>
</catalog>
Expected output:
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<cd>
<title rowmerge="f">Title 1</title>
<artist rowmerge="f">sample 1</artist>
<price rowmerge="f">1</price>
<year >1985</year>
</cd>
<cd>
<title rowmerge="F">Title 2</title>
<artist rowmerge="F">Sample 2</artist>
<price rowmerge="f">1</price>
<year>1988</year>
</cd>
<cd>
<title rowmerge="F">Title 3</title>
<artist rowmerge="F">Sample 3</artist>
<price rowmerge="F">3</price>
<year>1988</year>
</cd>
<cd>
<title rowmerge="F">Title 4</title>
<artist rowmerge="F">sample 4</artist>
<price rowmerge="F">4</price>
<year >1985</year>
</cd>
<cd>
<title rowmerge="F">Title 4</title>
<artist rowmerge="F">Sample 5</artist>
<price rowmerge="F">4</price>
<year>1988</year>
</cd>
</catalog>
If rowmerge attribute is 'T' in the first cd for any tag (title/artist/price)then I need to copy the price value from first cd to next cd. I am new to xslt.
You first should read up on the XSLT Identity Template, which on its own will copy nodes from the source document to the output.
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
What this means is that you only need to write templates for the nodes you wish to transform. Considering just the price elements for now, you are trying to amend price elements which have a rowmerge set to "T" and which are empty. In this case, you want to copy the price from the first most preceding 'cd'. This is achieved like so:
<xsl:template match="price[#rowmerge='T'][not(normalize-space())]">
<price>
<xsl:apply-templates select="#*|node()"/>
<xsl:value-of select="../preceding-sibling::*[1]/price" />
</price>
</xsl:template>
So, it is very similar to the identity template, but it has the extra xsl:value-of statement to copy the value from the preceding node. Or rather the price value from the preceding node of the parent cd element.
Of course, you could repeat this template for each of the possible child elements of cd, but this would be a lot of repetitive coding. Better would be to have a more generic template to cover all cases:
<xsl:template match="*[#rowmerge='T'][not(normalize-space())]">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:value-of select="../preceding-sibling::*[1]/*[name() = name(current())]" />
</xsl:copy>
</xsl:template>
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="*[#rowmerge='T'][not(normalize-space())]">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:value-of select="../preceding-sibling::*[1]/*[name() = name(current())]" />
</xsl:copy>
</xsl:template>
<xsl:template match="#rowmerge[. = 'T']">
<xsl:attribute name="rowmerge">F</xsl:attribute>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Do note there is also a template to convert the rowmerge attributes from having a value of T to F.
EDIT: In answer to your comment, if you have to look more than one sibling back (that is to say, you have two consecutive elements that are empty), then try one of these two expressions
<xsl:value-of select="../preceding-sibling::*[*[name() = name(current())][normalize-space()]][1]/*[name() = name(current())]" />
<xsl:value-of select="(../preceding-sibling::*/*[name() = name(current())][normalize-space()])[last()]" />
EDIT 2: If the nodes contain more than just text, then to copy all the child elements, you use xsl:copy-of instead of xsl:value-of. For example...
<xsl:copy-of select="../preceding-sibling::*[1]/*[name() = name(current())]/node()" />
Note the use of node() on the end, to ensure only the child nodes are copied, not the price (for example) element itself.
I want to transform an input xml file using xslt to change the name of a particular element which can appear at different places of the xml tree.
I have an xml like the following,
<catalog>
<cd>
<ost:title>Empire Burlesque</ost:title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>
<ost:name>Columbia<ost:name>
</company>
<price>10.90</price>
<year>1985</year>
</cd>
</catalog>
I want to remove all the 'ost:' prefix from all the elements and keep everything else as it is using xslt. An example code will be appreciated.
In your example, you don't seem to have other namespaces than the one you want to remove. So, here is an example of an XSLT stylesheet, which removes all namespaces from elements (not just your ost:).
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- identity template: copy everything as is... -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- ... except for elements,
create a similarly named element without a namespace -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
It uses the identity transformation to copy everything as-is, but overrides that for elements to create an element with the same local name, but no namespace.
If you want to just remove your ost: namespace, you can include the namespace declaration for that namespace, and change the latter template to match ost:*.
This transformation is most general. It removes all elements and attributes from namespaces that are specified within a global parameter. It also removes all namespace nodes that are one of those namespaces:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my" >
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<my:delNs>
<ns>some:namespace1</ns>
<ns>some:namespace3</ns>
</my:delNs>
<xsl:variable name="vdelNS"
select="document('')/*/my:delNs/*"/>
<xsl:template match="*">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="namespace::*[not(.=$vdelNS)]"/>
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template priority="10" match=
"*[namespace-uri()=document('')/*/my:delNs/*]">
<xsl:element name="{local-name()}">
<xsl:copy-of select="namespace::*[not(.=$vdelNS)]"/>
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
<xsl:template match=
"#*[namespace-uri()=document('')/*/my:delNs/*]">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the following XML document (based on the provided, but corrected to be well-formed and extended to contain 3 namespaces and elements and attributes in them):
<catalog xmlns:ost="some:namespace1"
xmlns:x="some:namespace2"
xmlns:y="some:namespace3">
<cd>
<ost:title>Empire Burlesque</ost:title>
<y:artist>Bob Dylan</y:artist>
<country>USA</country>
<company ost:type="big">
<ost:name>Columbia</ost:name>
</company>
<x:price>10.90</x:price>
<year>1985</year>
</cd>
</catalog>
the wanted result is produced:
<catalog xmlns:x="some:namespace2">
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company type="big">
<name>Columbia</name>
</company>
<x:price>10.90</x:price>
<year>1985</year>
</cd>
</catalog>
Explanation:
The first two templates are almost equivalent to the identity rule, but they do not copy namespace nodes for the namespaces specified in <my:delNs>.
The last two templates are for all elements and attributes that belong to a namespace listed under <my:delNs>. Only in these two templates names are actually changed to only local names.