cancel
Showing results for 
Search instead for 
Did you mean: 

XSLT Error in CPI - net.sf.saxon.trans.XPathException

sacino
Discoverer
7,083

Hello Experts,


I need an XSLT mapping for a very large input file (1.6 million records) because the records need to be streamed and filtered.
I have created an XSLT mapping and tested it with XML Spy. Since I got the expected result, I implemented the mapping in my iFlow and now I get this error:

[CAMEL][IFLOW][EXCEPTION] : org.apache.camel.FailedToCreateRouteException: Failed to create route Process_1: Route(Process_1)[[From[ref:MessageFlow_14.endpoint]] -> [Rem... because of net.sf.saxon.s9api.SaxonApiException: Errors were reported during stylesheet compilation [CAMEL][IFLOW][CAUSE] : Cause: javax.xml.transform.TransformerConfigurationException: net.sf.saxon.s9api.SaxonApiException: Errors were reported during stylesheet compilation [CAMEL][IFLOW][CAUSE] : Cause: net.sf.saxon.s9api.SaxonApiException: Errors were reported during stylesheet compilation [CAMEL][IFLOW][CAUSE] : Cause: net.sf.saxon.trans.XPathException: Errors were reported during stylesheet compilation

This is the input file (in a very shortened form):

mapping-test.xml


And here is the XSLT I want to use to transform the file:



Can anyone please help me figure out what I did wrong?
I am still a beginner in CPI and have only worked with message mappings so far.

Thanks a lot already,
Sandra

Accepted Solutions (1)

Accepted Solutions (1)

MortenWittrock
SAP Mentor
SAP Mentor

Hi Sandra

First off: That the stylesheet works in XMLSpy but not in CPI is not necessarily as odd as it seems, since the underlying XSLT engines (also known as processors) are different. The one in CPI is called Saxon and it supports streaming. From a brief search, it seems that XMLSpy doesn't support streaming for now. In other words, XMLSpy would perform the transformation with the entire source document loaded into memory.

Second: The stylesheet compilation fails in CPI because the stylesheet's template rules are not streamable. There are many limitations on what you can do, if you want to stream the transformation. Which is fair enough, because the whole point of streaming is to avoid keeping the entire source document in memory. So you can't, for instance, access a previous element once you've passed it in the stream.

This page lists some of the restrictions (Saxonica is the company that created the Saxon XSLT processor). One of the big ones is this:

"The body of the template rule must contain at most one expression or instruction that reads the contents below the matched element (that is, children or descendants), and it must process the contents in document order."

This is very different from how you would normally write a template. One of the ways you can work around this restriction is by creating a copy of the current element and then working on the copy, which is now in memory and no longer read from the stream.

I've rewritten your stylesheet to do this, and this version of the stylesheet passes the streamability checks:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:mode streamable="yes"/>

<xsl:template match="/">
<root>
<xsl:apply-templates select="/root/data"/>
</root>
</xsl:template>

<xsl:template match="data">
<xsl:variable name="data" select="copy-of(.)"/>
<xsl:choose>
<xsl:when test="$data/BrandName = ('Lenovo', 'Acer', 'HP', 'Hewlett Packard Enterprise', 'Toshiba')">
<data>
<Lieferanten_Nr></Lieferanten_Nr>
<Price_List_ID>"10056"</Price_List_ID>
<MODE>DAILY</MODE>
<Artikel_Nr_Lieferant>"<xsl:value-of select="$data/ManufacturerPartNo"/>"</Artikel_Nr_Lieferant>
<Artikel_Nr_Hersteller>"<xsl:value-of select="$data/ManufacturerPartNo"/>"</Artikel_Nr_Hersteller>
<Artikel_Nr_EAN>"<xsl:value-of select="$data/EAN_UPC"/>"</Artikel_Nr_EAN>
<Artikelbezeichnung_kurz>"<xsl:value-of select="$data/Description"/>"</Artikelbezeichnung_kurz>
<Artikelbezeichnung_lang>"<xsl:value-of select="$data/Description2"/>"</Artikelbezeichnung_lang>
<Herstellername>"<xsl:value-of select="$data/BrandName"/>"</Herstellername>
<Warengruppe1>"<xsl:value-of select="$data/ItemGroupName"/>"</Warengruppe1>
<Warengruppe2></Warengruppe2>
<Preis_EVP_EUR><xsl:value-of select="$data/CustomersPrice"/></Preis_EVP_EUR>
<Waehrung>"EUR"</Waehrung>
<Mehrwertsteuersatz>"19"</Mehrwertsteuersatz>
</data>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:template>

</xsl:stylesheet>


(It wasn't entirely clear why you filter on those particular brand names, but I'm guessing there are more brand names in the original source document.)

This should give you a starting point. As you develop your stylesheet further, test it in CPI frequently, so that you can catch any compilation errors early. Check the Saxonica page to see if you've added anything that might cause the streamability checks to fail. If you can't find the problem, feel free to post a new question in here.

Good luck and have fun with CPI!

Regards,

Morten

sacino
Discoverer

Hello Morten,

thank you so much!
It works, I am thrilled.
This was my first experience with XSLT 3.0 and streaming. I didn't really understand about "select=copy-of" and I didn't find a helpful explanation for me with the different samples.
Thanks for the explanation and your tips, that really helps me a lot.
And yes you are right, in the original document are many more brands listed and we need only these 5. I have tested of course only with a small file.

So again thanks a lot for your time and work on this topic!

Regards,
Sandra

MortenWittrock
SAP Mentor
SAP Mentor
0 Kudos

Hi sacino

No problem. I'm glad you found it useful.

Regards,

Morten

Answers (0)