XSL how to exclude some children in output? - xslt

Beginner XSL question..and I know there are similar questions and answers posted, but I can't figure out how to apply them to my XSLT...
My source XML looks like (this is just a fragment of a much larger XML file)
<?xml version="1.0" encoding="UTF-8"?>
<COLLECTION><Release NAME="Release" TYPE="Unknown" STATUS="0">
<Transaction>
<TransactionNumber>4</TransactionNumber>
<ReleaseNumber>4</ReleaseNumber>
<PrimaryObjectID>OR:wt.part.WTPart:121581:416986630-1502721046884-982634822-1-0-0-127#ODIGettingStarted.tri.co.uk</PrimaryObjectID>
<CreatedBy>orgadmin</CreatedBy>
<CreatedDate>2017-09-27 08:34:31 EDT</CreatedDate>
<Locale>en_US</Locale>
<Destination>CRP1</Destination>
</Transaction>
</Release>
I want to exclude the Locale and Destination nodes from the output.
My complete solution will be more complex that requires me to split the XML into three, hence my use of but the relevant code I have so far is :-
<?xml version = "1.0" encoding = "UTF-8"?>
<xsl:stylesheet version = "2.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:param name ="outputFileDir" select="'file:///D:/workspace/TPHMOT_xsl/TPHMOT_xsl/xsl_output'"/>
<xsl:template match ="/">
<xsl:result-document href="{$outputFileDir}/ESI_ItemMasters_1.xml" method="xml" indent="yes">
<COLLECTION>
<xsl:apply-templates select="COLLECTION/Release"/>
</COLLECTION>
</xsl:result-document>
<xsl:result-document href="{$outputFileDir}/ESI_ConfigurableItem_1.xml" method="xml" indent="yes">
<COLLECTION>
<xsl:apply-templates select="COLLECTION/Release"/>
</COLLECTION>
</xsl:result-document>
<xsl:result-document href="{$outputFileDir}/ESI_GenericBOM_1.xml" method="xml" indent="yes">
<COLLECTION>
<xsl:apply-templates select="COLLECTION/Release"/>
</COLLECTION>
</xsl:result-document>
</xsl:template>
<xsl:template match="Release">
<xsl:copy-of select="self::node()"/>
</xsl:template>
</xsl:stylesheet>
This outputs
<?xml version="1.0" encoding="UTF-8"?>
<COLLECTION>
<Release NAME="Release" TYPE="Unknown" STATUS="0">
<Transaction>
<TransactionNumber>4</TransactionNumber>
<ReleaseNumber>4</ReleaseNumber>
<PrimaryObjectID>OR:wt.part.WTPart:121581:416986630-1502721046884-982634822-1-0-0-127#ODIGettingStarted.tri.co.uk</PrimaryObjectID>
<CreatedBy>orgadmin</CreatedBy>
<CreatedDate>2017-09-27 08:34:31 EDT</CreatedDate>
<Locale>en_US</Locale>
<Destination>CRP1</Destination>
</Transaction>
</Release>
</COLLECTION>
How can I adapt my XSL to exclude Locale and Destination child nodes?
Many thanks in advance for any help offered!

Instead of copying your complete element in
<xsl:template match="Release">
<xsl:copy-of select="self::node()"/>
</xsl:template>
you only need to use the identity transformation
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
and then empty template(s) to prevent the elements you don't want from being copied:
<xsl:template match="Locale | Destination"/>

Related

Duplicate a node and child elements if it occurs only once

I need to duplicate a node and its child elements, if it occurs only once in the xml. Otherwise, the xml shouldn't be modified. For ex, in the below xml, if <dataList> occurs only once then duplicate it one more time. If not, don't change the xml at all. Only XSLT 1.0 please.
Input XML
<?xml version="1.0" encoding="UTF-8"?>
<API>
<Token/>
<root>
<dataList>
<addressOne>1</addressOne>
<addressTwo/>
<bkdn/>
</dataList>
</root>
</API>
Expected output xml
<?xml version="1.0" encoding="UTF-8"?>
<API>
<Token/>
<root>
<dataList>
<addressOne>1</addressOne>
<addressTwo/>
<bkdn/>
</dataList>
<dataList>
<addressOne>1</addressOne>
<addressTwo/>
<bkdn/>
</dataList>
</root>
</API>
As per my understanding here i want to solve it:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root">
<xsl:copy>
<xsl:choose>
<!-- If you are looking for the dataList occurance then use count -->
<xsl:when test="count(dataList) = 1">
<!-- If you are looking for the dataList/addressOne value = 1 occurance then use below -->
<!-- <xsl:when test="dataList/addressOne=1"> -->
<xsl:apply-templates select="dataList"/>
<xsl:apply-templates select="dataList"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="dataList"/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Question on XSLT to eliminate parent node where child is in CDATA section

I have an original XML message which looks like below.
<Message>
<Header>
<MsgVerNo>1.0</MsgVerNo>
<SourceId>XXX</SourceId>
<MsgRefNo>1234567890</MsgRefNo>
<LoginId>007</LoginId>
</Header>
<Body>
<![CDATA[<txn>
<id>1234567</id>
<name>XXXX</name>
</txn>]]>
</Body>
</Message>
I wish to transform it as below. and should be removed while child nodes should be remained. In addition, CData which wrap should be removed as well.
<Message>
<MsgVerNo>1.0</MsgVerNo>
<SourceId>XXX</SourceId>
<MsgRefNo>1234567890</MsgRefNo>
<LoginId>007</LoginId>
<txn>
<record>
<id>1234567</id>
<name>XXXX</name>
</record>
</txn>
</Message>
I tried to use below XSLT. But the output is not desired.
<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:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Header">
<xsl:copy-of select="node()"/>
</xsl:template>
<xsl:template match="Body">
<xsl:copy-of select="node()"/>
</xsl:template>
</xsl:stylesheet>
Output
<Message>
<MsgVerNo>1.0</MsgVerNo>
<SourceId>XXX</SourceId>
<MsgRefNo>1234567890</MsgRefNo>
<LoginId>007</LoginId>
<txn>
<id>1234567</id>
<name>XXXX</name>
</txn>
</Message>
I have no luck so far. Any help from expert please. Thanks.
The CDATA element means that the data in between will not be interpreted as XML by your parse. This is specifically the purpose of CDATA. You can send character data, including < > without the parser trying to interpret it and potentially failing.
For all intents and purposes your Body node just contains text. You can read it as text and even strip the CDATA tag, however that still leaves you with text that looks like XML instead of something that is interpreted as XML.
You could use the following to get the contents, however if you wanted to further parse the contents of the body element you would have to pass it on to another XSLT for example.
<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:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Header">
<xsl:copy-of select="node()"/>
</xsl:template>
<xsl:template match="Body">
<xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:template>
</xsl:stylesheet>

Identity transformation XSLT

I have an incoming XML document. I just need to modify value of one element example <ID> element in this below incoming XML document. I basically need to check for element called <ID> if the value is without any hyphen it will take as it is and if the value contains hyphen(-) then i need to take the value before hyphen (-) ex- 4314141
Incoming XML document:
<Message>
<ID>4314141-324234</ID>
<EMAIL>abc</EMAIL>
</Message>
I am using this below XSL to do do this but it is not working as expected.
XSL:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dp="http://www.datapower.com/extensions"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
extension-element-prefixes="dp"
exclude-result-prefixes="dp" >
<xsl:variable name="uuid" select="dp:variable('var://context/txn/uuid')" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/ID">
<xsl:copy>
<ID><xsl:value-of select="substring-before($ID, ' -')" /></ID>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="ID"/>
</xsl:stylesheet>
Let me know how i can do this.
without any hyphen it will take as it is
This will do your identity-copy template.
if the value contains hyphen(-) then i need to take the value before hyphen (-)
<xsl:template match="ID[contains(., '-')]">
<xsl:copy>
<xsl:value-of select="substring-before(., '-')" />
</xsl:copy>
</xsl:template>
Friendly advice: Please be carefull with / in your matching patterns.
Just use this template overriding the identity rule:
<xsl:template match="ID/text()[contains(., '-')]">
<xsl:value-of select="substring-before(., '-')"/>
</xsl:template>
Here is the complete 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()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ID/text()[contains(., '-')]">
<xsl:value-of select="substring-before(., '-')"/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<Message>
<ID>4314141-324234</ID>
<EMAIL>abc</EMAIL>
</Message>
the wanted, correct result is produced:
<Message>
<ID>4314141</ID>
<EMAIL>abc</EMAIL>
</Message>

Add attribute to a DOM NodeList node using XSLT

I have to add one attribute Publisher="Penguin" to the nodes from a NodeList : The input xml looks like:
<Rack RackNo="1">
<Rows>
<Row RowNo="1" NoOfBooks="10"/>
<Row RowNo="2" NoOfBooks="15"/>
<Rows>
</Rack>
The output xml lookslike:
<Rack RackNo="1">
<Rows>
<Row RowNo="1" NoOfBooks="10" Publisher="Penguin"/>
<Row RowNo="2" NoOfBooks="15" Publisher="Penguin"/>
<Rows>
</Rack>
The xsl i wrote is :
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<Order>
<xsl:copy-of select = "Rack/#*"/>
<xsl:for-each select="Rows/Row">
<OrderLine>
<xsl:copy-of select = "Row/#*"/>
<xsl:attribute name="Publisher"></xsl:attribute>
<xsl:copy-of select = "Row/*"/>
</OrderLine>
</xsl:for-each>
<xsl:copy-of select = "Rack/*"/>
</Order>
</xsl:template>
</xsl:stylesheet>
This doesnt return the desired output.
Any help will be much appreciated.
Thanks in advance guys.
This is a job for the XSLT identity transform. On its own it simple creates a copy of all the nodes in your input XML
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
All you need to do is add an extra template to match Row element, and add a Publisher attribute to it. It might be good to first parameterise the publisher you wish to add
<xsl:param name="publisher" select="'Penguin'" />
You then create the matching template as follows:
<xsl:template match="Row">
<OrderLine Publisher="{$publisher}">
<xsl:apply-templates select="#*|node()"/>
</OrderLine>
</xsl:template>
Note the use of "Attribute Value Templates" to create the Publisher attribute. The curly braces indicate it is an expression to be evaluated. Also note in your XSLT it looks like you are renaming the elements too, so I have done this in my XSLT as well. (If this is not the case, simply replace OrderLine back with Row.
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="publisher" select="'Penguin'" />
<xsl:template match="Rack">
<Order>
<xsl:apply-templates />
</Order>
</xsl:template>
<xsl:template match="Rows">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="Row">
<OrderLine Publisher="{$publisher}">
<xsl:apply-templates select="#*|node()"/>
</OrderLine>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to your XML, the following is output
<Order>
<OrderLine Publisher="Penguin" RowNo="1" NoOfBooks="10"></OrderLine>
<OrderLine Publisher="Penguin" RowNo="2" NoOfBooks="15"></OrderLine>
</Order>

Adding element in middle of xml using xslt

Below is the actual xml:
<?xml version="1.0" encoding="utf-8"?>
<employee>
<Name>ABC</Name>
<Dept>CS</Dept>
<Designation>sse</Designation>
</employee>
And i want the output as below:
<?xml version="1.0" encoding="utf-8"?>
<employee>
<Name>ABC</Name>
<Age>34</Age>
<Dept>CS</Dept>
<Domain>Insurance</Domain>
<Designation>sse</Designation>
</employee>
Is this possible to add XML element in between using xslt?
Please give me sample!
Here is an XSLT 1.0 stylesheet that will do what you asked:
<?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="Name">
<xsl:copy-of select="."/>
<Age>34</Age>
</xsl:template>
<xsl:template match="Dept">
<xsl:copy-of select="."/>
<Domain>Insurance</Domain>
</xsl:template>
</xsl:stylesheet>
Obviously the logic will vary depending on where you will be getting the new data from, and where it needs to go. The above stylesheet merely inserts an <Age> element after every <Name> element, and a <Domain> element after every <Dept> element.
(Limitation: if your document could have <Name> or <Dept> elements within other <Name> or <Dept> elements, only the outermost ones will have this special processing. I don't think you intend for your document to have this kind of recursive structure, so it wouldn't affect you, but it's worth mentioning just in case.)
I have modified few things in the existing stylesheet ,it will allow you to choose the specific element and update in your xml.
<?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="Name[1]">
<xsl:copy-of select="."/>
<Age>34</Age>
</xsl:template>
<xsl:template match="Dept[1]">
<xsl:copy-of select="."/>
<Domain>Insurance</Domain>
</xsl:template>
</xsl:stylesheet>
XML:
<?xml version="1.0" encoding="utf-8"?>
<employee>
<Name>ABC</Name>
<Dept>CS</Dept>
<Designation>sse</Designation>
<Name>CDE</Name>
<Dept>CSE</Dept>
<Designation>sses</Designation>
</employee>