XSLT - match doesn't seem to work - xslt

I have this line in my transform:
<xsl:template match="simplesect[#kind='since']">
When I apply it to the following:
<detaileddescription>
<para><simplesect kind="since">
<para>yesterday</para>
</simplesect></para></detaileddescription>
I expect it to work.
However, I noticed that a space needs to exist between and tags.
So the following matches where the above doesn't
<detaileddescription>
<para> <simplesect kind="since">
<para>yesterday</para>
</simplesect></para></detaileddescription>
I'm stumped. Any ideas why or is here a call I make? Right now, the only solution I have is to find every instance of <para><simplesect #kind="since">and changing it to <para> <simplesect #kind=since. Notice the space between <para> and <simplesect>

This stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="simplesect[#kind='since']">
<modified><xsl:apply-templates/></modified>
</xsl:template>
</xsl:stylesheet>
when applied to the first input, produces:
<?xml version="1.0" encoding="utf-8"?>
<detaileddescription>
<para>
<modified>
<para>yesterday</para>
</modified>
</para>
</detaileddescription>

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>

Adding missing nodes to XML via XSLT

I'm using a tool to import XML files into Dynamics NAV, but some parties providing the XML files skip empty nodes. My tool (external) can not handle those situation so I want to include XSLT to add the missing nodes. The xslt works fine for 1 node, but adding multiple nodes does not work. So I must be doing something wrong.
I'm building an integration to Dynamics NAV to insert Sales Orders. The orders are delivered from multiple parties using a XML file. However some of the parties providing the XML do not list all nodes in their XML file, they skip the empty ones. I'm using a tool build within Dynamics NAV (Add-on from other vendor) to import those files. However some XML files go wrong because of the fact that some (empty) nodes are missing in the XML file. I know this is an issue within the add-on but I need a solution on short notice. So created an XSLT to add the missing nodes. It works fine with 1 missing node, but it is not able to add both missing nodes. I'm not that familiar with XSLT so most of the times it is trial & error. Perhaps someone can help me with this.
This is the XML file format that is provided, The nodes that are sometimes missing is the DeliveryParty node and the DeliveryAddress part.
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<Orders>
<Order>
<Partner>
<SenderEANCode>9999999999999</SenderEANCode>
<RecipientEANCode>9999999999999</RecipientEANCode>
</Partner>
<OrderHeader>
<OrderVersion>008</OrderVersion>
<OrderTypeCode>220</OrderTypeCode>
<Document>
<DocumentNumber>34034040</DocumentNumber>
<Date>2019-04-18</Date>
</Document>
<DeliveryDate>2019-04-24</DeliveryDate>
<CompleteDelivery>YES</CompleteDelivery>
<Supplier>9999999999999</Supplier>
<Buyer>9999999999999</Buyer>
<Invoicee>9999999999999</Invoicee>
<DeliveryParty>9999999999999</DeliveryParty>
<DeliveryAddress>
<DeliveryName>Private Customer</DeliveryName>
<DeliveryStreet>Teststraat</DeliveryStreet>
<DeliveryCity>TestCity</DeliveryCity>
<DeliveryCountry>NL</DeliveryCountry>
<DeliveryTelNo></DeliveryTelNo>
<DeliveryEmail>test#test.com</DeliveryEmail>
</DeliveryAddress>
</OrderHeader>
<OrderLine>
<LineItemNumber>1</LineItemNumber>
<GTIN>9999999999999</GTIN>
<OrderedQuantity>
<Quantity>260</Quantity>
</OrderedQuantity>
</OrderLine>
</Order>
</Orders>
Sometimes the DeliveryParty node is missing and other times the DeliveryAddress part including subnodes is missing. I created the following XSLT to add those nodes but as it is trail and error I need some help to fix this. I'm a novice to XSLT, I can so some small changes but I do not use it frequently so knowledge is fading away quickly.
<?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:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Orders/Order/OrderHeader[not(DeliveryParty)]">
<xsl:copy-of select="*"/>
<DeliveryParty/>
</xsl:template>
<xsl:template match="Orders/Order/OrderHeader[not(//DeliveryAddress)]">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<DeliveryAddress>
<DeliveryName></DeliveryName>
<DeliveryStreet></DeliveryStreet>
<DeliveryPostalCode></DeliveryPostalCode>
<DeliveryCity></DeliveryCity>
<DeliveryCountry></DeliveryCountry>
<DeliveryTelNo></DeliveryTelNo>
<DeliveryEmail></DeliveryEmail>
</DeliveryAddress>
</xsl:copy>
</xsl:template>
With above mentioned XSLT the DeliveryAddress node with it's subnodes is added but the deliveryparty is not.
When the file is delivered like this:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<Orders>
<Order>
<Partner>
<SenderEANCode>9999999999999</SenderEANCode>
<RecipientEANCode>9999999999999</RecipientEANCode>
</Partner>
<OrderHeader>
<OrderVersion>008</OrderVersion>
<OrderTypeCode>220</OrderTypeCode>
<Document>
<DocumentNumber>34034040</DocumentNumber>
<Date>2019-04-18</Date>
</Document>
<DeliveryDate>2019-04-24</DeliveryDate>
<CompleteDelivery>YES</CompleteDelivery>
<Supplier>9999999999999</Supplier>
<Buyer>9999999999999</Buyer>
<Invoicee>9999999999999</Invoicee>
</OrderHeader>
<OrderLine>
<LineItemNumber>1</LineItemNumber>
<GTIN>9999999999999</GTIN>
<OrderedQuantity>
<Quantity>260</Quantity>
</OrderedQuantity>
</OrderLine>
</Order>
</Orders>
The outcome should be this:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<Orders>
<Order>
<Partner>
<SenderEANCode>9999999999999</SenderEANCode>
<RecipientEANCode>9999999999999</RecipientEANCode>
</Partner>
<OrderHeader>
<OrderVersion>008</OrderVersion>
<OrderTypeCode>220</OrderTypeCode>
<Document>
<DocumentNumber>34034040</DocumentNumber>
<Date>2019-04-18</Date>
</Document>
<DeliveryDate>2019-04-24</DeliveryDate>
<CompleteDelivery>YES</CompleteDelivery>
<Supplier>9999999999999</Supplier>
<Buyer>9999999999999</Buyer>
<Invoicee>9999999999999</Invoicee>
<DeliveryParty></DeliveryParty>
<DeliveryAddress>
<DeliveryName></DeliveryName>
<DeliveryStreet></DeliveryStreet>
<DeliveryCity></DeliveryCity>
<DeliveryCountry></DeliveryCountry>
<DeliveryTelNo></DeliveryTelNo>
<DeliveryEmail></DeliveryEmail>
</DeliveryAddress>
</OrderHeader>
<OrderLine>
<LineItemNumber>1</LineItemNumber>
<GTIN>9999999999999</GTIN>
<OrderedQuantity>
<Quantity>260</Quantity>
</OrderedQuantity>
</OrderLine>
</Order>
</Orders>
How about:
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="OrderHeader">
<xsl:copy>
<xsl:apply-templates/>
<xsl:if test="not(DeliveryParty)">
<DeliveryParty/>
</xsl:if>
<xsl:if test="not(DeliveryAddress)">
<DeliveryAddress>
<DeliveryName/>
<DeliveryStreet/>
<DeliveryPostalCode/>
<DeliveryCity/>
<DeliveryCountry/>
<DeliveryTelNo/>
<DeliveryEmail/>
</DeliveryAddress>
</xsl:if>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

XSLT - Get String between commas

How can I get the value 'four' in XSLT?
<root>
<entry>(one,two,three,four,five,six)</entry>
</root>
Thanks in advance.
You didn't specify the XSLT version, so I assume version 2.0.
I also assume that word four is only a "marker", stating from which place
take the result string (between the 3rd and 4th comma).
To get the fragment you want, you can:
Use tokenize function to "cut" the whole content of entry
into pieces, using a comma as the cutting pattern.
Take the fourth element of the result array.
This expression can be used e.g. in a template matching entry.
So the example script can look like below:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="entry">
<xsl:copy>
<xsl:value-of select="tokenize(., ',')[4]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()"/></xsl:copy>
</xsl:template>
</xsl:transform>
For your input XML it gives:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<entry>four</entry>
</root>

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>

How do I make the value of one element a new attribute to the root?

I want to transform a markup like <root><a><c>CA</c></a></root> to <root juris="CA"><a><c>CA</c></a></root>
If you could specify more about the schema that is allowed, something more specific can be written. With what was given, something like this should work though:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/root">
<xsl:copy>
<xsl:attribute name="juris"><xsl:value-of select="./a/c"/></xsl:attribute>
<xsl:copy-of select="*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
As it stands, I would not be surprised if this led to issues with more complex inputs.