Use XSLT to wrap all nodes inside newly-inserted node - xslt

How do I use XSLT to transform this XML:
<root>
<base_currency_code>ZAR</base_currency_code>
<base_discount_amount>0</base_discount_amount>
<base_discount_invoiced>0</base_discount_invoiced>
</root>
into this:
<root>
<items>
<base_currency_code>ZAR</base_currency_code>
<base_discount_amount>0</base_discount_amount>
<base_discount_invoiced>0</base_discount_invoiced>
</items>
</root>

This is a rather trivial problem. You should have known the answer after spending an hour or less with an XSLT tutorial:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/root">
<xsl:copy>
<items>
<xsl:copy-of select="*"/>
</items>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

Fetch internal tags in the xml using XSLT

when I execute the below stylesheet
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|#*" >
<xsl:copy>
<xsl:variable name="manu" select="node()|#*"/>
<xsl:apply-templates select="$manu"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I'm getting the below output:-
<?xml version="1.0" encoding="UTF-8" ?>
<A1>
<B1>
<FILES>
<StudentData>
<Student>0001</Student>
<Student>0002</Student>
</StudentData>
</FILES>
</B1>
</A1>
Now I want to select only the StudentData:-
Expected Output:-
<StudentData>
<Student>0001</Student>
<Student>0002</Student>
</StudentData>
How to match this in XSLT?
There a lots of ways. i.e. you could match on root and then just do a copy of the element using the xpath to that element 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 omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:copy-of select="A1/B1/FILES/StudentData"/>
</xsl:template>
</xsl:stylesheet>
Why using an identity template? As far as I understand your problem, the simple solution is this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="StudentData" >
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
If you want to filter the empty lines out of your output, add a template matching the text nodes for that purpose:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="StudentData" >
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="text()"/>
<!-- this tricky template prevents empty white lines in output -->
</xsl:stylesheet>

Move entire xml code inside another xml tag

I have xml code and I want to put two xml tags in the beginning of the xml code so all the code will come under those two tags
Any ideas how to achieve that with XSLT? I am a newby to XSLT and tried the whole day in vain... Any help would really be appreciated.
I have an XML that looks like this
<?xml version="1.0" encoding="UTF-8"?>
<ns0:PCN xmlns:ns0="abc">
<PCD>
<PC>
<TID>123456</TID>
<Sequence>1</Sequence>
<Type>M</Type>
</PC>
<PC>
<TID>123457</TID>
<Sequence>2</Sequence>
<Type>M</Type>
</PC>
</PCD>
</ns0:PCN>
and I need to transform it to look like this:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Messages xmlns:ns0="xyz">
<ns0:Message1>
<ns0:PCN xmlns:ns0="abc">
<PCD>
<PC>
<TID>123456</TID>
<Sequence>1</Sequence>
<Type>M</Type>
</PC>
<PC>
<TID>123457</TID>
<Sequence>2</Sequence>
<Type>M</Type>
</PC>
</PCD>
</ns0:PCN>
</ns0:Message1>
</ns0:Messages>
Please find below my attempted code. This is my first try and I have written this after referring to several codes of xslt . It is not giving me desired result.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- Identity transform -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ns0:PCN">
<xsl:copy-of select="."/>
<ns0:Messages xmlns:ns0="xyz"/>
<ns0:Message1/>
</xsl:template>
</xsl:stylesheet>
How about simply:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<ns0:Messages xmlns:ns0="xyz">
<ns0:Message1>
<xsl:copy-of select="*"/>
</ns0:Message1>
</ns0:Messages>
</xsl:template>
Demo: https://xsltfiddle.liberty-development.net/3NJ38ZK
</xsl:stylesheet>

XSLT (XML to XML) output contains non-matching data from source

Using Visual Studio to perform the transform during development the resulting xml contains text from the source xml in the destination that was contained in tags that do not match my template criteria
I was expecting that my select Group in the first template to find any elements named Group that are immediate children of CrystalReport and pass them along in the apply template call. I understood that the match filter on my second template would only take in Group's that have an attribute of Level=1 and write them out. I'd expect everything else to be ignored.
Why does "not this" appear in my output?
source
<?xml version="1.0" encoding="utf-8" ?>
<!--
UPDATE: Note that adding the xmlns attribute causes all output
to disappear unless you use Chris's second solution.
-->
<CrystalReport xmlns="urn:crystal-reports:schemas:report-detail" >
<Group Level="1">
<GroupHeader>
<Section>
<Field FieldName="apple" />
</Section>
</GroupHeader>
<Group Level="2">
not this
</Group>
</Group>
</CrystalReport>
transform
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/CrystalReport">
<root>
<xsl:apply-templates select="Group"/>
</root>
</xsl:template>
<xsl:template match="Group[#Level='1']/GroupHeader">
<tag1><xsl:value-of select="Section/Field/#FieldName"/></tag1>
</xsl:template>
</xsl:stylesheet>
output
<?xml version="1.0" encoding="utf-8"?>
<root>
<tag1>apple</tag1>
not this
</root>
You are facing the problem where the built in templates of the XML parser are coming into play. You are apply templates to all of the Group elements, but only catching one of them with your own templates. The other is handled by the default templates which out put the values of all nodes. I suggest that you change
<xsl:template match="/CrystalReport">
into
<xsl:template match="/">
This will override the root default templates which are producing the extra output. You can find more on the built in template rules at http://www.w3.org/TR/xslt#built-in-rule
Then override the basic text() template so you final XSLT looks a bit like this
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<root>
<xsl:apply-templates select="CrystalReport/Group"/>
</root>
</xsl:template>
<xsl:template match="Group[#Level='1']/GroupHeader">
<tag1>
<xsl:value-of select="Section/Field/#FieldName"/>
</tag1>
</xsl:template>
<xsl:template match="text()"/>
UPDATE
Or even simpler you could just match the desired elements and use something like this
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/CrystalReport">
<root>
<xsl:apply-templates select="Group[#Level='1']/GroupHeader"/>
</root>
</xsl:template>
<xsl:template match="GroupHeader">
<tag1>
<xsl:value-of select="Section/Field/#FieldName"/>
</tag1>
</xsl:template>
This will leave the default text() templates in place which can be very handy.
Try
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:crystal="urn:crystal-reports:schemas:report-detail">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/crystal:CrystalReport">
<root>
<xsl:apply-templates select="crystal:Group[#Level='1']/crystal:GroupHeader"/>
</root>
</xsl:template>
<xsl:template match="crystal:GroupHeader">
<tag1>
<xsl:value-of select="crystal:Section/crystal:Field/#FieldName"/>
</tag1>
</xsl:template>

XSLT select unique node

this is driving me crazy, for xslt newbie like me.
Input:
<root>
<a><name>kyle</name></a>
<b><name>stan</name></b>
<b><name>wendy</name></b>
<b><name>cece</name></b>
</root>
Expected output:
<root>
<a><name>kyle</name></a>
<b><name>stan</name></b>
</root>
I was asked to return first unique node under 'root', how do I do that?
Either xslt 1.0 or 2.0 is fine.
Thank you so much!!!!
XSLT 2.0 solution :
<?xml version="2.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<root>
<xsl:for-each-group select="root/*" group-by="local-name()">
<xsl:copy-of select="."/>
</xsl:for-each-group>
</root>
</xsl:template>
</xsl:stylesheet>
Output:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a>
<name>kyle</name>
</a>
<b>
<name>stan</name>
</b>
</root>
You can match any element that has a preceding sibling with the same name and not output anything.
Example XSLT:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*/*[preceding-sibling::*[name() = current()/name()]]"/>
</xsl:stylesheet>
Output (using Saxon 9 HE):
<root>
<a>
<name>kyle</name>
</a>
<b>
<name>stan</name>
</b>
</root>

How to ignore namespaces during xsl translation time

I have an xml as below.
<?xml version="1.0" encoding="UTF-8"?>
<books xmlns="http://www.books.com/SRK">
<name>English</name>
</books
I required the following output after translation using xsl .
<?xml version="1.0" encoding="UTF-8"?>
<books>
<name>English</name>
</books>
I need an xsl to ignore the namespace.I have tried something but its not working with namespace.
I need your help.Your help would be appreciated.
This 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="#*|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>
when applied to the provided XML document:
<books xmlns="http://www.books.com/SRK">
<name>English</name>
</books>
produces the wanted, correct result:
<books>
<name>English</name>
</books>
Its working only if i include the above templates if i add some other templates other than the above one then the translation is not at all working .None of the template getting executed.
Probably you are missing the declaration of the namespace for the book element. Example:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:b="http://www.books.com/SRK"
exclude-result-prefixes="b">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- #dimitre's answer templates -->
<xsl:template match="b:name">
<!-- your template for name -->
</xsl:template>
</xsl:stylesheet>
Moreover, make sure to use local-name() function to get the name of an element without the related namespace.
Example
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:b="http://www.books.com/SRK"
exclude-result-prefixes="b">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="b:input">
<xsl:element name="{local-name(.)}">
<xsl:apply-templates select="b:name"/>
</xsl:element>
</xsl:template>
<xsl:template match="b:name">
<xsl:element name="{local-name()}">
<xsl:value-of select="."/>
</xsl:element>
<lhs>
<xsl:apply-templates select="following-sibling::b:lhs/b:evaluate"/>
</lhs>
</xsl:template>
<xsl:template match="b:evaluate">
Something to evaluate...
</xsl:template>
</xsl:stylesheet>
gets:
<?xml version="1.0" encoding="UTF-8"?>
<input>
<name>English</name>
<lhs>
Something to evaluate...
</lhs>
</input>
Second Example
You can create a separate transform called local-identity.xsl containing #Dimitre solution. Then you can import it in your transform. Because you have a namespace, to match elements you must change all your XPaths including the prefix you will declare in the transform, as in the following example:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:brl="http://www.xyz.com/BRL"
exclude-result-prefixes="brl"
version="1.0">
<xsl:import href="local-identity.xsl"/>
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/brl:rule">
<!-- do your staff, select using brl prefix -->
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>