XSLT delete elements in the XML - xslt-1.0

Can someone explain how I can transform Input XML to Output XML shown below using XSLT.
I need to delete the 'RElement' in the XML that doesn't have 'Type' element with Value 'Account' in its descendant child elements.
'RElement' is recursive.
Input XML-
<RElement>
<Type>Group</Type>
<RElement>
<Type>Group</Type>
<RElement>
<Type>Account</Type>
</RElement>
</RElement>
<RElement>
<Type>Group</Type>
<RElement>
<Type>Group</Type>
<RElement>
<Type>Group</Type>
</RElement>
</RElement>
</RElement>
</RElement>
Output XML-
<RElement>
<Type>Group</Type>
<RElement>
<Type>Group</Type>
<RElement>
<Type>Account</Type>
</RElement>
</RElement>
</RElement>

You can try this:
<?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"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="RElement[not(.//Type[text() = 'Account'])]" />
</xsl:stylesheet>
Basically it's Identity transform with an empty template for RElement elements that doesn't have 'Type' element with Value 'Account' in its descendant child elements.
Edited to add Ian Robert's empty template, which is better:
<xsl:template match="RElement[not(.//Type = 'Account')]" />

Related

Replacing a child node with another node without affecting its sub node

I need to replace the child node with another node without affecting its sub nodes, I tried matching the child node but was unable to
This is the xml format
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Header>
<MessageId>{A124-B421-C325-D467}</MessageId>
<Action>find</Action>
</Header>
<Body>
<MessageParts xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Run xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/Run">
<RunObject class="entity">
<A1>NA</A1>
<A2>False</A2>
<Object class="entity">
<A3>02</A3>
</Object>
<A4>ER</A4>
</RunObject>
</Run>
</MessageParts>
</Body>
</Envelope>
This is the xml format that i require
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Header>
<MessageId>{A124-B421-C325-D467}</MessageId>
<Action>find</Action>
</Header>
<Body>
<Document>
<Item>
<A1>NA</A1>
<A2>False</A2>
<Base>
<A3>02</A3>
</Base>
<A4>ER</A4>
</Item>
</Document>
</Body>
</Envelope>
This is the code that i through which i tried to change the format
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:m="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"
xmlns:r="http://schemas.microsoft.com/dynamics/2008/01/documents/Run"
exclude-result-prefixes="m r">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- move all elements to no namespace -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<!-- rename MessageParts to Document + skip the Run wrapper -->
<xsl:template match="m:MessageParts">
<Document>
<xsl:apply-templates select="r:Run/*"/>
</Document>
</xsl:template>
<!-- rename RunObject to Item + reorder child nodes -->
<xsl:template match="r:RunObject[#class='entity']">
<Item>
<xsl:apply-templates select="r:A1" />
<xsl:apply-templates select="r:A2" />
<xsl:template match="r:Object[#class='entity']>
<Base>
<xsl:apply-templates select="r:A3" />
</Base>
</xsl:Template>
<xsl:apply-templates select="r:A4" />
</Item>
</xsl:template>
</xsl:stylesheet>
I tried matching the Object element but was unable to since i am already matching its parent element that is RunObject
Your mistake is that you cannot define a template inside of a template. So move the <xsl:template match="r:Object[#class='entity']> to the root level and add an <xsl:apply-templates select="r:Object" /> at its place.
The stylesheet could look like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:m="http://schemas.microsoft.com/dynamics/2011/01/documents/Message" xmlns:r="http://schemas.microsoft.com/dynamics/2008/01/documents/Run" exclude-result-prefixes="m r">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- move all elements to no namespace -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<!-- rename MessageParts to Document + skip the Run wrapper -->
<xsl:template match="m:MessageParts">
<Document>
<xsl:apply-templates select="r:Run/*"/>
</Document>
</xsl:template>
<!-- rename RunObject to Item + reorder child nodes -->
<xsl:template match="r:RunObject[#class='entity']">
<Item>
<xsl:apply-templates select="r:A1" />
<xsl:apply-templates select="r:A2" />
<xsl:apply-templates select="r:Object" />
<xsl:apply-templates select="r:A4" />
</Item>
</xsl:template>
<xsl:template match="r:Object[#class='entity']">
<Base>
<xsl:apply-templates select="r:A3" />
</Base>
</xsl:template>
</xsl:stylesheet>
Its output is (nearly) as desired:
<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<Header>
<MessageId>{A124-B421-C325-D467}</MessageId>
<Action>find</Action>
</Header>
<Body>
<Document>
<Item>
<A1>NA</A1>
<A2>False</A2>
<Base>
<A3>02</A3>
</Base>
<A4>ER</A4>
</Item>
</Document>
</Body>
</Envelope>
I added the adjective "nearly", because this result has no namespace, while the sample of your desired outcome is in the namespace
xmlns:m="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"
But because you defined a template that removes the namespaces of all elements, I did ignore that. I simply assume that this part of your question is erroneous.
If you like to change that, restrict the first template and add a general identity template:
<!-- move all elements to no namespace -->
<xsl:template match="*[namespace-uri() != 'http://schemas.microsoft.com/dynamics/2011/01/documents/Message']">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
This would make your output containing the root namespace:
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Header>
<MessageId>{A124-B421-C325-D467}</MessageId>
<Action>find</Action>
</Header>
<Body>
<Document xmlns="">
<Item>
<A1>NA</A1>
<A2>False</A2>
<Base>
<A3>02</A3>
</Base>
<A4>ER</A4>
</Item>
</Document>
</Body>
</Envelope>

Removing Parent Nodes without affecting child nodes

The XML output i am getting is not in the format that i require so tried some code to change the format but was unable to
This is the output that i am getting
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Header>
<MessageId>{A124-B421-C325-D467}</MessageId>
<Action>find</Action>
</Header>
<Body>
<MessageParts xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Run xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/Run">
<RunObject class="entity">
<A1>NA</A1>
<A2>False</A2>
<A3>02</A3>
<A4>ER</A4>
</RunObject>
<RunObject class="entity">
<A1>NA</A1>
<A2>False</A2>
<A3>03</A3>
<A4>ER</A4>
</RunObject>
</Run>
</MessageParts>
</Body>
</Envelope>
This is the XML output that i require
<?xml version="1.0" encoding="UTF-8"?>
<Document>
<Item>
<A3>NA</A3>
<A4>False</A4>
<A2>02</A2>
<A1>ER</A1>
</Item>
<Item>
<A3>NA</A3>
<A4>False</A4>
<A2>03</A2>
<A1>ER</A1>
</Item>
</Document>
This is the code that i have used to change the format of the xml that i was getting
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:m="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"
xmlns:r="http://schemas.microsoft.com/dynamics/2008/01/documents/Run"
exclude-result-prefixes="m r">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- move all elements to no namespace -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="m:Envelope"/>
<xsl:template match="m:Header"/>
<xsl:template match="m:MessageId"/>
<xsl:template match="m:Action"/>
<xsl:template match="m:Body"/>
<!-- rename MessageParts to Document + skip the Run wrapper -->
<xsl:template match="m:MessageParts">
<Document>
<xsl:apply-templates select="r:Run/*"/>
</Document>
</xsl:template>
<!-- rename RunObject to Item + reorder child nodes -->
<xsl:template match="r:RunObject[#class='entity']">
<Item>
<xsl:apply-templates select="r:A3" />
<xsl:apply-templates select="r:A4" />
<xsl:apply-templates select="r:A2" />
<xsl:apply-templates select="r:A1" />
</Item>
</xsl:template>
</xsl:stylesheet>
I tried the above code but was not able to change the format of the xml
Instead of:
<xsl:template match="m:Envelope"/>
that removes the Envelope node and all its descendants, you need to remove only the Envelope wrapper and continue processing its contents:
<xsl:template match="m:Envelope">
<xsl:apply-templates/>
</xsl:template>
Likewise for the other wrappers. In fact, you could replace all of these:
<xsl:template match="m:Envelope"/>
<xsl:template match="m:Header"/>
<xsl:template match="m:MessageId"/>
<xsl:template match="m:Action"/>
<xsl:template match="m:Body"/>
with:
<xsl:template match="m:*">
<xsl:apply-templates select="*"/>
</xsl:template>

How to remove Namespaces when copying nodes to a different node in XML through XSLT1.0?

I'm new to XSL and i'm trying to copy a node of XML to another node in the same XML using XSLT. I can able to transform the file as expected but the XMLNS attribute is getting added to the destination which I don't want.
I have tried all the option of copy-namespaces='no' using XSLT2.0 but that doesn't work. Also, I cant have a prefix of namespaces in the XSLT and use exclude namespaces to avoid xmlns because the incoming XML file is dynamic and namespaces keep on changing. I can't have all the namespaces declared as a prefix in XSLT.
Incoming XML:
<?xml version="1.0" encoding="UTF-8"?>
<PropertySet>
<Message>
<NotificationHeader>
<BusinessId></BusinessId>
<CorrelationId>0201201916:21:24CKG3N</CorrelationId>
<SourceName></SourceName>
<SourceId></SourceId>
<EventType></EventType>
<SecurityIdentifierId></SecurityIdentifierId>
<ClientRequestId></ClientRequestId>
<TargetId>ESB</TargetId>
<EventTime></EventTime>
<RequestUser></RequestUser>
</NotificationHeader>
<EventList>
<Event>
<PayLoad>
<ListOfActionIo xmlns="http://www.test.com/IO">
<Action>
<ActivityId>4-309C7WV</ActivityId>
<ActivitySRId></ActivitySRId>
<ActivityTemplateId></ActivityTemplateId>
<ActivityUID>4-309C7WV</ActivityUID>
<Category> Notification</Category>
<Comment></Comment>
<Type>Action</Type>
<ListOfDestination>
<Destination>
<DestinationName>NSW Aboriginal Education Consultative Group Incorporated</DestinationName>
</Destination>
<Destination>
<DestinationName>NSW Aboriginal Education Consultative Group Incorporated</DestinationName>
</Destination>
</ListOfDestination>
</Action>
</ListOfActionIo>
</PayLoad>
</Event>
</EventList>
<DestinationList>
<Destination>
<DestinationName></DestinationName>
<DestinationId></DestinationId>
</Destination>
</DestinationList>
</Message>
</PropertySet>
XSLT Code:
<?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" method="xml" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//*[name()='Message']/*[name()='DestinationList']//*[name()='Destination']"></xsl:template>
<!--Copy the destination from one node to another node-->
<xsl:template match="//*[name()='Message']/*[name()='DestinationList']">
<xsl:copy>
<xsl:copy-of select="//*[name()='Message']/*[name()='EventList']//*[name()='Destination']"/>
</xsl:copy>
</xsl:template>
<!--Remove the original node from where the destination was copied-->
<xsl:template match="//*[name()='Message']/*[name()='EventList']//*[name()='ListOfDestination']"></xsl:template>
</xsl:stylesheet>
Output:
<PropertySet>
<Message>
<NotificationHeader>
<BusinessId/>
<CorrelationId>0201201916:21:24CKG3N</CorrelationId>
<SourceName/>
<SourceId/>
<EventType/>
<SecurityIdentifierId/>
<ClientRequestId/>
<TargetId>ESB</TargetId>
<EventTime/>
<RequestUser/>
</NotificationHeader>
<EventList>
<Event>
<PayLoad>
<ListOfActionIo xmlns="http://www.test.com/IO">
<Action>
<ActivityId>4-309C7WV</ActivityId>
<ActivitySRId/>
<ActivityTemplateId/>
<ActivityUID>4-309C7WV</ActivityUID>
<Category> Notification</Category>
<Comment/>
<Type>Action</Type>
</Action>
</ListOfActionIo>
</PayLoad>
</Event>
</EventList>
<DestinationList>
<Destination xmlns="http://www.test.com/IO">
<DestinationName>NSW Aboriginal Education Consultative Group Incorporated</DestinationName>
</Destination>
<Destination xmlns="http://www.test.com/IO">
<DestinationName>NSW Aboriginal Education Consultative Group Incorporated</DestinationName>
</Destination>
</DestinationList>
</Message>
</PropertySet>
Expected Output:
Without the Namespaces on the copied node.
<PropertySet>
<Message>
<NotificationHeader>
<BusinessId/>
<CorrelationId>0201201916:21:24CKG3N</CorrelationId>
<SourceName/>
<SourceId/>
<EventType/>
<SecurityIdentifierId/>
<ClientRequestId/>
<TargetId>ESB</TargetId>
<EventTime/>
<RequestUser/>
</NotificationHeader>
<EventList>
<Event>
<PayLoad>
<ListOfActionIo xmlns="http://www.test.com/IO">
<Action>
<ActivityId>4-309C7WV</ActivityId>
<ActivitySRId/>
<ActivityTemplateId/>
<ActivityUID>4-309C7WV</ActivityUID>
<Category> Notification</Category>
<Comment/>
<Type>Action</Type>
</Action>
</ListOfActionIo>
</PayLoad>
</Event>
</EventList>
<DestinationList>
<Destination>
<DestinationName>NSW Aboriginal Education Consultative Group Incorporated</DestinationName>
</Destination>
<Destination>
<DestinationName>NSW Aboriginal Education Consultative Group Incorporated</DestinationName>
</Destination>
</DestinationList>
</Message>
</PropertySet>
You are asking the wrong question. You don't want to "remove namespaces" - you want to change the namespace of transformed elements. This is akin to renaming the elements.
Now, since your example is confusing (and too long), consider the following:
XML
<root>
<colors>
<color>red</color>
<color>blue</color>
</colors>
<shapes xmlns="some-unknown-namespace">
<shape>circle</shape>
<shape>square</shape>
</shapes>
<sizes>
<size>small</size>
<size>large</size>
</sizes>
</root>
To change the namespace of shapes (and its descendants, that inherit it!) to no-namespace, without knowing in advance what the original namespace will be, you can do:
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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[ancestor-or-self::*[name()='shapes']]">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<root>
<colors>
<color>red</color>
<color>blue</color>
</colors>
<shapes>
<shape>circle</shape>
<shape>square</shape>
</shapes>
<sizes>
<size>small</size>
<size>large</size>
</sizes>
</root>
Added:
lets modify the code to move the shapes into sizes and remove the shapes node.
OK, let's do that:
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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="sizes">
<xsl:copy>
<xsl:apply-templates/>
<!-- move the shapes into sizes -->
<xsl:apply-templates select="../*[name()='shapes']/*"/>
</xsl:copy>
</xsl:template>
<!-- remove the shapes element -->
<xsl:template match="*[name()='shapes']"/>
<xsl:template match="*[ancestor::*[name()='shapes']]">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<root>
<colors>
<color>red</color>
<color>blue</color>
</colors>
<sizes>
<size>small</size>
<size>large</size>
<shape>circle</shape>
<shape>square</shape>
</sizes>
</root>

XSLT sort data in variable and save them to another variable

I have a problem. I have the following XML
<countryList>
<country>
<name>Afghanistan</name>
<population>29117000</population>
<area>654329</area>
</country>
<country>
<name>Albania</name>
<population>3195000</population>
<area>28748</area>
</country>
<country>
<name>Algeria</name>
<population>35423000</population>
<area>2381741</area>
</country>
<country>
<name>Andorra</name>
<population>84082</population>
<area>468</area>
</country>
</countryList>
I have one question. All I need to do is divide population/area and sort these divisions for each country. However, I tried this
<xsl:variable name="Podiel">
<xsl:value-of select="../population div ../area"/>
</xsl:variable>
<xsl:variable name="PodielPodiel">
<xsl:for-each select="$Podiel">
<xsl:sort select="." data-type="number" order="descending"/>
</xsl:for-each>
</xsl:variable>
but I still get error
The 'select' expression does not evaluate to a node set.
no result for data1.xml
Any help? I just want to know the maximum of all divisions.
Thanks.
Not sure whether the issue has been resolved, however for using node-set in XSLT 1.0, extension functions have to be used. Please refer to node-set extension for additional details on it's usage in XSLT 1.0.
If at all the country list in the input XML needs to be sorted on the value of population div area, the number() function can be used within <xsl:sort>. Declaring variables and calculating the additional values and then accessing the variable is not required.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="xml" />
<xsl:strip-space elements="*" />
<!-- identity transform -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="countryList">
<xsl:copy>
<xsl:apply-templates select="country">
<xsl:sort select="number(population div area)" data-type="number" order="descending" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Output
<countryList>
<country>
<name>Andorra</name>
<population>84082</population>
<area>468</area>
</country>
<country>
<name>Albania</name>
<population>3195000</population>
<area>28748</area>
</country>
<country>
<name>Afghanistan</name>
<population>29117000</population>
<area>654329</area>
</country>
<country>
<name>Algeria</name>
<population>35423000</population>
<area>2381741</area>
</country>
</countryList>

Rename tag and remove attribute

I have an XML document (generated by WiX heat) where I want to remove the root element name while removing the attribute. The source tree looks like this
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="HELP" />
</Fragment>
</Wix>
I figured out how to rename the node, but this doesn't remove the unnecessary attribute xmlns.
<xsl:template match='/wix:Wix'>
<Include>
<xsl:copy-of select="#*|node()"/>
</Include>
</xsl:template>
<!-- Even this template doesn't suppress the attribute xmlns -->
<xsl:template match='#xmlns'/>
I event removed the #*| from the select clause. But this doesn't have any effect.
How can I produce with XSLT 1.0 the following desired output?
<Include>
<Fragment>
<DirectoryRef Id="HELP" />
</Fragment>
</Include>
this doesn't remove the unnecessary attribute xmlns.
xmlnsis not an attribute - it's a namespace, a part of the node's name. If you don't want it in the output, you cannot copy the input nodes that are in a namespace - you must create new nodes instead, for example:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wix="http://schemas.microsoft.com/wix/2006/wi"
exclude-result-prefixes="wix">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/wix:Wix">
<Include>
<xsl:apply-templates/>
</Include>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>