How change a Node Position using XSLT - xslt

I have a problem with the XML Web response i am getting. The Inner Loop warehouseItems have a node leadTimeCumulative and leadTimeDays coming on the first and Second Position for the FIrst time, for the Rest its coming last. I need to keep these node always in the Last position in side the Loop.
Example
<Envelope>
<Body>
<searchItemResponse>
<status>
<statusCode>Success</statusCode>
</status>
<itemList>
<itemWithWarehouses>
<item>
<originOfData>SME</originOfData>
<itemNumbers>
<shortNumber>115632</shortNumber>
<tssArticleNumber>PT0401450-T46N</tssArticleNumber>
</item>
<warehouseItems>
<leadTimeCumulative>14</leadTimeCumulative>
<leadTimeDays>14</leadTimeDays>
<warehouse>
<code>GA01</code>
</warehouse>-
<stockItem>-
<quantities>
<quantityAvailable>0</quantityAvailable>
<quantityOnHand>0</quantityOnHand>
</quantities>
</stockItem>-
<stockClass>
<group>MTO</group>
</stockClass>
</warehouseItems>-
<warehouseItems>-
<warehouse>
<code>GL01</code>
</warehouse>-
<stockItem>-
<quantities>
<quantityAvailable>0</quantityAvailable>
<quantityOnHand>0</quantityOnHand>
</quantities>
</stockItem>-
<stockClass>
<group>MTO</group>
</stockClass>
<leadTimeCumulative>14</leadTimeCumulative>
<leadTimeDays>14</leadTimeDays>
</warehouseItems>-
<warehouseItems>-
<warehouse>
<code>GS01</code></warehouse>-
<stockItem>-
<quantities>
<quantityAvailable>0</quantityAvailable>
<quantityOnHand>0</quantityOnHand>
</quantities>
</stockItem>-
<stockClass>
<group>MTO</group>
</stockClass>
<leadTimeCumulative>10</leadTimeCumulative>
<leadTimeDays>10</leadTimeDays>
</warehouseItems>-
</itemWithWarehouses>
</itemList>
</searchItemResponse>
</Body>
</Envelope>
I need any XSLT for changing the position of the node leadTimeCumulative and leadTimeDays to the Last like the rest of the Loop Structure. Please help me.
The above part is solved.
I need an improvement along with this.The tssArticleNumber node need to copy to the respective warehouse items with a different node name like "Item No". Please give me another XSLT for this. Thanks

try the following stylesheet:
<?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:strip-space elements="*"/>
<!-- this is called an identity template -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- a template to override warehouseItems -->
<xsl:template match="warehouseItems">
<xsl:copy>
<!-- apply templates except leadTimeCumulative and leadTimeDays -->
<xsl:apply-templates select="node()[not(self::leadTimeCumulative) and not(self::leadTimeDays)]|#*"/>
<Item_No><xsl:value-of select="../item//tssArticleNumber"/></Item_No>
<xsl:apply-templates select="leadTimeCumulative"/>
<xsl:apply-templates select="leadTimeDays"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

xsl:apply-templates returns nothing − what am I missing?

I have a simple XML response, like
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<searchRetrieveResponse xmlns="http://www.loc.gov/zing/srw/">
<numberOfRecords>1</numberOfRecords>
<records>
<record>
<recordData>
<kitodo xmlns="http://meta.kitodo.org/v1/">
<metadata name="key1">value1</metadata>
<metadata name="key2">value2</metadata>
<metadata name="key3">value3</metadata>
</kitodo>
</recordData>
</record>
</records>
</searchRetrieveResponse>
which I want to transform to this by XSLT
<?xml version="1.0" encoding="utf-8"?>
<mets:mdWrap xmlns:kitodo="http://meta.kitodo.org/v1/"
xmlns:mets="http://www.loc.gov/METS/"
xmlns:srw="http://www.loc.gov/zing/srw/"
MDTYPE="OTHER"
OTHERMDTYPE="Kitodo">
<mets:xmlData>
<kitodo:kitodo>
<kitodo:metadata name="key1">value1</kitodo:metadata>
<kitodo:metadata name="key2">value2</kitodo:metadata>
<kitodo:metadata name="key3">value3</kitodo:metadata>
</kitodo:kitodo>
</mets:xmlData>
</mets:mdWrap>
That is, I want to remove the outside tree searchRetrieveResponse/records/record/recordData, replace it with mdWrap/xmlData and move the contained data node there.
I have a quite short XSLT for it:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:kitodo="http://meta.kitodo.org/v1/" xmlns:mets="http://www.loc.gov/METS/" xmlns:srw="http://www.loc.gov/zing/srw/">
<xsl:output method="xml" indent="yes" encoding="utf-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="srw:recordData">
<mets:mdWrap MDTYPE="OTHER" OTHERMDTYPE="Kitodo">
<mets:xmlData>
<xsl:apply-templates select="#*|node()"/>
</mets:xmlData>
</mets:mdWrap>
</xsl:template>
<!-- pass-through rule -->
<xsl:template match="#*|node()">
<xsl:apply-templates select="#*|node()"/>
</xsl:template>
</xsl:stylesheet>
However, what I get is:
<?xml version="1.0" encoding="utf-8"?>
<mets:mdWrap xmlns:kitodo="http://meta.kitodo.org/v1/"
xmlns:mets="http://www.loc.gov/METS/"
xmlns:srw="http://www.loc.gov/zing/srw/"
MDTYPE="OTHER"
OTHERMDTYPE="Kitodo">
<mets:xmlData/>
</mets:mdWrap>
Obviously, the template match="srw:recordData" does match, otherwise I would get an empty result. However, the contained apply-templates doesn’t output anything. (I also tried an <xsl:apply-templates/> without a select="" attribute, but it doesn’t output anything either.) What am I missing?
XSLT processor is net.sf.saxon.TransformerFactoryImpl (Java)
I think nothing happens when you are applying templates inside xmlData. There are no templates that would match descendant nodes.
Try using copy-of:
<xsl:template match="srw:recordData">
<mets:mdWrap MDTYPE="OTHER" OTHERMDTYPE="Kitodo">
<mets:xmlData>
<xsl:copy-of select="kitodo:kitodo"/>
</mets:xmlData>
</mets:mdWrap>
</xsl:template>
The problem is not with the xsl:apply-templates instruction. It is with the template being applied. Your "pass-through rule" does not write anything to the output. You probably meant to have the identity transform template in that place - which goes like this:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>

Replace xsi:nil=“true” with open and close tags

I need to do the following transformation in order to get a message pass through a integration broker which does not understand xsi:nil=“true”. I know that for strings having some thing like <abc></abc> is not same as <abc xsi:nil=“true”/> but I have no option.
My input XML:
<PART>
<LENGTH_UOM xsi:nil="1"/>
<WIDTH xsi:nil="1"/>
</PART>
Expected outcome:
<PART>
<LENGTH_UOM><LENGTH_UOM>
<WIDTH></WIDTH>
</PART>
Please let me know your suggestions.
To remove all xsi:nil attributes combine the identity template with an empty template matching xsi:nil.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://xsi.com">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="node()|#*"> <!-- identity template -->
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="#xsi:nil" /> <!-- empty template -->
</xsl:stylesheet>
If you only want to remove those whose value is true use the following empty template instead.
<xsl:template match="#xsi:nil[.='1' or .='true']" />
Concerning the opening and closing tag topic I suggest reading this SO question in which Martin Honnen states that (in the comments of the answer):
I am afraid whether an empty element is marked up as or or is not something that matters with XML and is usually not something you can control with XSLT processors.

XSLT: How to remove elements of a resulting result tree fragment while copying?

My goal is to extract the contents of the SOAP body, f.e. the ElementsToExtract node - but the node name can basically be arbitrary:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Header>
<MessageId>52DF2371-4094-4408-A3EA-42D73FD1B7A3</MessageId>
</soap:Header>
<soap:Body>
<ElementsToExtract>
...
<RemoveMe>...</RemoveMe>
<RemoveMeAlso>...</RemoveMeAlso>
...
</ElementsToExtract>
</soap:Body>
</soap:Envelope>
While I'm extracting the contents, I want to get rid of two elements that all my source documents have in common - say RemoveMe and RemoveMeAlso. As there's a chance that the deeper nested nodes may be called the same, they must only be stripped from the layer below the ElementsToExtract node. How would I formulate that expression?
Here's what I did up to now:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="soap exsl">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="SoapHeaderContents" select="exsl:node-set(soap:Envelope/soap:Header/*)"/>
<xsl:variable name="SoapBodyContents" select="exsl:node-set(soap:Envelope/soap:Body/*)"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="$SoapBodyContents"/>
</xsl:template>
<!-- This is global, how to restrict to the ElementsToExtract element? -->
<xsl:template match="node()[name() = 'RemoveMe']"/>
<xsl:template match="node()[name() = 'RemoveMeAlso']"/>
</xsl:stylesheet>
I also played with the node-set() function, having read that one can not modify result tree fragments (they're only text nodes?), but I don't quite understand how to address the resulting nodes of that set. So the nodes weren't removed:
<xsl:template match="/">
<xsl:apply-templates select="$SoapBodyContents"/>
<xsl:apply-templates select="$SoapBodyContents/RemoveMe" mode="m1"/>
</xsl:template>
<xsl:template name="StripRemoveMe" match="RemoveMe" mode="m1"/>
I also read some parts of the specification, but to no avail. I'm lost for clues. Can someone direct me to the right approach?
Would this work for you:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
exclude-result-prefixes="soap">
<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>
<!-- skip soap wrappers -->
<xsl:template match="/soap:Envelope">
<xsl:apply-templates select="soap:Body/ElementsToExtract"/>
</xsl:template>
<!-- remove unwanted elements -->
<xsl:template match="ElementsToExtract/RemoveMe | ElementsToExtract/RemoveMeAlso"/>
</xsl:stylesheet>
In the (unlikely) case you don't know the name of the ElementsToExtract element, you could use:
<!-- skip soap wrappers -->
<xsl:template match="/soap:Envelope">
<xsl:apply-templates select="soap:Body/*"/>
</xsl:template>
<!-- remove unwanted elements -->
<xsl:template match="soap:Body/*/RemoveMe | soap:Body/*/RemoveMeAlso"/>
Some quick thoughts.
You create variables for storing the SOAP header and body. These are already in the input document, so it makes more sense to just write templates that match these.
Although you create a variable for the SOAP header, you never use it anywhere.
If you try to apply templates in succession, as in your sample XSL code, you will get all the output nodes from the first apply-templates, and then all the output nodes from the next apply-templates. If these nodes are meant to be interleaved in any way, this approach will not produce viable output.
Here's a revised version of your sample input XML, adding in a couple elements that we want to keep.
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Header>
<MessageId>52DF2371-4094-4408-A3EA-42D73FD1B7A3</MessageId>
</soap:Header>
<soap:Body>
<ElementsToExtract>
<KeepMe>This text will persist in the output.</KeepMe>
<RemoveMe>This is text that will be removed.</RemoveMe>
<RemoveMeAlso>This will also vanish from the output.</RemoveMeAlso>
<OtherElementToKeep>And this one will also be kept.</OtherElementToKeep>
</ElementsToExtract>
</soap:Body>
</soap:Envelope>
Here's what we'd want as output:
<?xml version="1.0" encoding="utf-8"?>
<ElementsToExtract>
<KeepMe>This text will persist in the output.</KeepMe>
<OtherElementToKeep>And this one will also be kept.</OtherElementToKeep>
</ElementsToExtract>
This XSL 1.0 code will do the job. I'm guessing from your post that you're not familiar with XSL processing flow, so I've added comments to help explain what's going on.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
version="1.0"
exclude-result-prefixes="soap">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" indent="yes"/>
<!-- The `/` matches the _logical root_ of the input file. This is
basically equivalent to the start of the file, NOT the first element.
This is a common place to start processing in XSL. -->
<xsl:template match="/">
<!-- We just apply templates. In your case, we know already that
we DON'T want to process everything: we want to leave certain
things out, including a lot of the outermost elements. So
we specify what to target in the `select` statement. -->
<xsl:apply-templates select="soap:Envelope/soap:Body/ElementsToExtract"/>
</xsl:template>
<!-- This is the "identity" template, so called because it
just copies over applicable matches identically.
A template with a more-specific match statement takes
precedence. -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Here, we specify exactly those elements that are in the
processing flow, and that we want to exclude from the
output. Since `soap:Header` etc. are NOT in the processing
flow (their element trees were never included in a preceding
call to `apply-templates`), we don't need to worry about those. -->
<xsl:template match="RemoveMe | RemoveMeAlso"/>
</xsl:stylesheet>
Note that the outermost element in the output is ElementsToExtract. This element will include the xmlns:soap="http://www.w3.org/2003/05/soap-envelope" namespace declaration, even though this namespace isn't used in any of the output elements (at least, for this small sample input XML).
If you can use XSL 2.0+ and you want to remove this namespace from the output, you could add the copy-namespaces="no" attribute to the <xsl:copy> element.

XSLT code to remove xml tag

I have an incoming XML like below: I need to remove the <shoeboxImage> tag from the incoming below XML.
Incoming XML Input:
<attachReceipt>
<baseMessage>
<returnCode>200</returnCode>
</baseMessage>
<payload>
<returnCode>0</returnCode>
<shoeboxItem>
<shoeboxImageCount>2</shoeboxImageCount>
<shoeboxImages>
<shoeboxImage>
<name>receiptImage.jpg</name>
</shoeboxImage>
<shoeboxImage>
<name>receiptImage.jpg</name>
</shoeboxImage>
</shoeboxImages>
</shoeboxItem>
</payload>
</attachReceipt>
Expected Output:
<attachReceipt>
<baseMessage>
<returnCode>200</returnCode>
</baseMessage>
<payload>
<returnCode>0</returnCode>
<shoeboxItem>
<shoeboxImageCount>2</shoeboxImageCount>
<shoeboxImages>
<name>receiptImage.jpg</name>
<name>receiptImage.jpg</name>
</shoeboxImages>
</shoeboxItem>
</payload>
</attachReceipt>
Need some xslt code snippet to do this.
I don't have the necessary software installed to actually test this, but this should work:
<xsl:template match="shoeboxImage">
<xsl:apply-templates select="*|text()"/>
</xsl:template>
The idea is that when a shoeboxImage element is encountered, it generates nothing for the element itself, and just continues with its children.
You need to have an identity template and a template that will remove the element shoeboxImage but will retain its descendants.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<!-- identity template -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- template override for the element shoeboxImage -->
<xsl:template match="shoeboxImage">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>

xslt: move all siblings inside the first one

I've searched through similar questions, but couldn't make any of the suggestions to work. I have the following xml I need to modify it
<XDB>
<ROOT>
<KEY><ID>12345</ID><DATE>5/10/2011</DATE></KEY>
<PERSONAL><ID>1</ID><INFO><LASTNAME>Smith</LASTNAME>...</INFO></PERSONAL>
<CONTACT><ID>1</ID><EMAIL>asmith#yahoo.com</EMAIL>...</CONTACT>
</ROOT>
<ROOT>
<KEY><ID>98765</ID><DATE>5/10/2013</DATE></KEY>
<CONTACT><ID>2</ID><EMAIL>psmithton#yahoo.com</EMAIL>...</CONTACT>
</ROOT>
...
</XDB>
And it needs to look like this:
<XDB>
<ROOT>
<KEY><ID>12345</ID><DATE>5/10/2011</DATE>
<PERSONAL><ID>1</ID><INFO><LASTNAME>Smith</LASTNAME>...</INFO></PERSONAL>
<CONTACT><ID>1</ID><EMAIL>asmith#yahoo.com</EMAIL>...</CONTACT>
</KEY>
</ROOT>
<ROOT>
<KEY><ID>98765</ID><DATE>5/10/2013</DATE>
<CONTACT><ID>2</ID><EMAIL>psmithton#yahoo.com</EMAIL>...</CONTACT>
</KEY>
</ROOT>
...
</XDB>
I need to make 2...n siblings as children of the first 'key' sibling. Essentially, i need to remove the closing < /KEY> and put it before the closing < /ROOT>. I would appreciate your help.
Thanks.
Following xslt based on Identity transform could make this job
<?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" version="1.0" encoding="UTF-8" indent="yes"/>
<!-- Copy everything you find... -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<!-- ... but if you find first element inside ROOT ... -->
<xsl:template match="ROOT/node()[1]">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
<!-- ... copy its sibling into it ... -->
<xsl:copy-of select="following-sibling::*" />
</xsl:copy>
</xsl:template>
<!-- ignore other elements inside ROOT element since they are copied in template matching first element -->
<xsl:template match="ROOT/node()[position() > 1]" />
</xsl:stylesheet>