XSLT – Compare node values and pass attributes if nodes are equal - xslt

I am trying to do something that is probably very simple, but my very rudimentary xslt is not up to it.
Given the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<MyLists>
<List1>
<Place01 ctr="PTG">Lisbon</Place01>
<Place02 ctr="SPA">Madrid</Place02>
<Place03 ctr="FRA">Paris</Place03>
<Place04 ctr="ENG">York</Place04>
</List1>
<List2>
<Item01 type="country">Italy</Item01>
<Item02 type="person">John</Item02>
<Item03 type="city">York</Item03>
<Item04 type="city" subtype="capital">Madrid</Item04>
</List2>
</MyLists>
I would like to compare the text nodes from <List1> and <List2>, and, whenever their values are the same, pass, for each element, the attributes from <List2> to the corresponding items in <List1>, in order to get:
<?xml version="1.0" encoding="UTF-8"?>
<MyLists>
<List1>
<Place01 ctr="PTG">Lisbon</Place01>
<Place02 ctr="SPA" type="city" subtype="capital">Madrid</Place02>
<Place03 ctr="FRA">Paris</Place03>
<Place04 ctr="ENG" type="city">York</Place04>
</List1>
<List2>
<Item01 type="country">Italy</Item01>
<Item02 type="person">John</Item02>
<Item03 type="city">York</Item03>
<Item04 type="city" subtype="capital">Madrid</Item04>
</List2>
</MyLists>
Ideally, I'd like to be able to copy whichever attributes these element possess, without having to specify them.
Many thanks in advance!

If I understand this correctly, you could do something like:
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:key name="match" match="Item" use="." />
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Place">
<xsl:copy>
<xsl:copy-of select="key('match', .)/#*"/>
<xsl:apply-templates/>
</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>

How do you split XMLs in mule? splitter?

Im a bit new to splitting XMLs, Can you help me create multiple XMLs from one input? do I need to use splitters? XSLT? also, can i plud in the message id in the xml as well?
Input
<?xml version="1.0"?>
<StockMovementDataRequest xmlns:p1="urn:ams.com.au:dynamo:3pl:am:SAP_AM_I_005:StockMovement" xmlns:a="http://www.edi.com.au/EnterpriseService">
<Header>
<From>Warehouse</From>
<To>Location</To>
<Unique_ID>idm1468201212</Unique_ID>
<DateTimeStamp>2016-04-13T11:55:30.263+10:00</DateTimeStamp>
</Header>
<StockMovementData>
<Serialised_Material>YES</Serialised_Material>
<DateTime>2016-04-13T11:55:30.263+10:00</DateTime>
<From_Location>30-80</From_Location>
<To_Location>client</To_Location>
<Material>7CAGL3G00</Material>
<Serial>700030011</Serial>
<Quantity>100</Quantity>
</StockMovementData>
<StockMovementData>
<Serialised_Material>YES</Serialised_Material>
<DateTime>2016-04-13T11:55:30.263+10:00</DateTime>
<From_Location>30-80</From_Location>
<To_Location>client</To_Location>
<Material>7CAGL3G00</Material>
<Serial>700029911</Serial>
<Quantity>100</Quantity>
</StockMovementData>
</StockMovementDataRequest>
output
<?xml version="1.0"?>
<StockMovementDataRequest xmlns:p1="urn:ams.com.au:dynamo:3pl:am:SAP_AM_I_005:StockMovement"
xmlns:a="http://www.edi.com.au/EnterpriseService/">
<Header>
<From>warehouse</From>
<To>client</To>
<Unique_ID>idm1467386212</Unique_ID>
<DateTimeStamp>2016-04-13T11:55:30.263+10:00</DateTimeStamp>
</Header>
<StockMovementData>
<Serialised_Material>YES</Serialised_Material>
<DateTime>2016-04-13T11:55:30.263+10:00</DateTime>
<From_Location>30-80</From_Location>
<To_Location>client</To_Location>
<Material>7CAGL3G00</Material>
<Serial>700030011</Serial>
<Quantity>100</Quantity>
</StockMovementData>
</StockMovementDataRequest>
and
<?xml version="1.0"?>
<StockMovementDataRequest xmlns:p1="urn:ams.com.au:dynamo:3pl:am:SAP_AM_I_005:StockMovement"
xmlns:a="http://www.edi.com.au/EnterpriseService/">
<Header>
<From>warehouse</From>
<To>client</To>
<Unique_ID>idm1467386212</Unique_ID>
<DateTimeStamp>2016-04-13T11:55:30.263+10:00</DateTimeStamp>
</Header>
<StockMovementData>
<Serialised_Material>YES</Serialised_Material>
<DateTime>2016-04-13T11:55:30.263+10:00</DateTime>
<From_Location>30-80</From_Location>
<To_Location>client</To_Location>
<Material>7CAGL3G00</Material>
<Serial>700029911</Serial>
<Quantity>100</Quantity>
</StockMovementData>
</StockMovementDataRequest>
Any thoughts?
If your processor supports XSLT-2.0, you can try the code below:
<?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="2.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:for-each select="StockMovementDataRequest/StockMovementData">
<xsl:result-document href="{concat('output', position(), '.xml')}">
<StockMovementDataRequest xmlns:p1="urn:ams.com.au:dynamo:3pl:am:SAP_AM_I_005:StockMovement" xmlns:a="http://www.edi.com.au/EnterpriseService">
<xsl:apply-templates select="preceding-sibling::Header"/>
<xsl:apply-templates select="."/>
</StockMovementDataRequest>
</xsl:result-document>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
it outputs, output1.xml and output2.xml based on your input.
Use Mule Splitter to split xml
<splitter expression="#[xpath3('//StockMovementDataRequest/StockMovementData',payload,'NODESET')]" doc:name="Splitter"/>
<mulexml:dom-to-xml-transformer doc:name="DOM to XML"/>
To aggregate the payload after splitter, use aggregator
<collection-aggregator doc:name="Collection Aggregator"/>
See more documentation here
https://docs.mulesoft.com/mule-user-guide/v/3.7/splitter-flow-control-reference

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>

Create xsl key by "joining" elements

<t>
<rendition xml:id="b">color: blue</rendition>
<rendition xml:id="r">color: red</rendition>
<tagUsage gi="p" render="b" />
<tagUsage gi="emph" render="r" />
</t>
How would I create an XSL 1.0 key into rendition elements based on #gi in the tagUsage element, joining rendition/#xml:id to tagUsage/#render? Something like
<xsl:key name="rendition-by-tagName"
match="rendition"
use="//tagUsage[#xml:id of rendition = #render of tagUsage]/#gi" />
so that given "p", the key would return the blue rendition; given "emph", the key would return the red rendition.
Use:
<xsl:key name="kRendByUsageGi" match="rendition"
use="../tagUsage[#render=current()/#xml:id]/#gi"/>
Here is a complete verification:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kRendByUsageGi" match="rendition"
use="../tagUsage[#render=current()/#xml:id]/#gi"/>
<xsl:template match="/">
<xsl:copy-of select="key('kRendByUsageGi', 'p')/text()"/>
========
<xsl:copy-of select="key('kRendByUsageGi', 'emph')/text()"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<t>
<rendition xml:id="b">color: blue</rendition>
<rendition xml:id="r">color: red</rendition>
<tagUsage gi="p" render="b" />
<tagUsage gi="emph" render="r" />
</t>
the wanted, correct result is produced:
color: blue
========
color: red
I found that the following, which uses a second key(), works with xsltproc, so, if that's your target processor, this should help. It doesn't work with Saxon, though.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name='kTagUsage' match='tagUsage' use='#render'/>
<xsl:key name="kRendByUsageGi" match="rendition"
use="key('kTagUsage', #xml:id)/#gi"/>
<xsl:template match="/">
<xsl:copy-of select="key('kRendByUsageGi', 'p')/text()"/>
========
<xsl:copy-of select="key('kRendByUsageGi', 'emph')/text()"/>
</xsl:template>
</xsl:stylesheet>

How to copy a certain node (with children) from a XML with XSLT in Biztalk specifying a custom namespace?

I need to copy a subnode from a XML to a certain node of a new XML in a Biztalk Map using XSLT.
Consider the following input XML:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:root xmlns:ns0="http://not/useful/data/">
<ns0:notuseful>
<ns0:foo></ns0:foo>
<ns0:foo2></ns0:foo2>
<ns0:blabla></ns0:blabla>
</ns0:notuseful>
<ns0:data>
<ns1:usefulDataList xmlns:ns1="http://useful/data/">
<ns1:usefulData>
<ns1:usefulChild1></ns1:usefulChild1>
<ns1:usefulChild2></ns1:usefulChild2>
<ns1:usefulChild3></ns1:usefulChild3>
<ns1:usefulChild4></ns1:usefulChild4>
<ns1:usefulChild5></ns1:usefulChild5>
</ns1:usefulData>
</ns1:usefulDataList>
</ns0:data>
<ns0:root>
What I need is to extract the node called "usefulDataList", so I need to copy it in a new XML like this one:
<?xml version="1.0" encoding="UTF-8"?>
<ns2:root2 xmln:ns2="http://new/xml">
<ns2:blabla>
<ns2:stuff />
</ns2:blabla>
<ns2:data>
<ns2:usefulDataList>
<ns2:usefulData>
<ns2:usefulChild1></ns2:usefulChild1>
<ns2:usefulChild2></ns2:usefulChild2>
<ns2:usefulChild3></ns2:usefulChild3>
<ns2:usefulChild4></ns2:usefulChild4>
<ns2:usefulChild5></ns2:usefulChild5>
</ns2:usefulData>
</ns2:usefulDataList>
</ns2:data>
</ns2:root2>
This should be done inside a Biztalk Functoid, as you see namespaces from source and target are diferent.
I'm an absolute beginner with XSLT, and I've been doing some tests, but I've something wrong with my XSLT expressions:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns2="http://new/xml">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template name="testTemplate" match="//*[local-name() = 'usefulDataList ']">
<xsl:element name="ns0:usefulDataList " namespace="">
<xsl:apply-templates mode="copy-no-ns" select="usefulDataList"/>
</xsl:element>
</xsl:template>
<xsl:template mode="copy-no-ns" match="*">
<xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-no-ns"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
I'd appreciate any tip, with XSLT or Biztalk mapper. I don't like linking a huge amount of fields one by one if I can solve it with a XSLT expression.
Greetings.
Beware you had a space in *[local-name() = 'usefulDataList ']" so that would never match. this works:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns2="http://new/xml">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<ns2:root>
<ns2:blabla>
<ns2:stuff />
</ns2:blabla>
<ns2:data>
<xsl:apply-templates mode="copy-no-ns" select="//*[local-name() = 'usefulDataList']"/>
</ns2:data>
</ns2:root>
</xsl:template>
<xsl:template mode="copy-no-ns" match="*">
<xsl:element name="ns2:{local-name(.)}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-no-ns"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>