Having difficulty in looping partNumber XSLT being applied shown below - xslt-2.0

Sample SOAP Input XML Given below:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Body>
<PullCustomerPartsPricingResponse xmlns="http://cdx.dealerbuilt.com/Api/0.99/">
<PullCustomerPartsPricingResult xmlns:a="http://schemas.datacontract.org/2004/07/DealerBuilt.BaseApi" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:CustomerPart>
<a:Placement>
<a:GroupId>10</a:GroupId>
</a:Placement>
<a:Attributes xmlns:b="http://schemas.datacontract.org/2004/07/DealerBuilt.Models.Parts">
<b:Description>PAD SET, RR.</b:Description>
<b:PartNumber>31500SB2100M</b:PartNumber>
<b:PartNumberFormatted>31500-SB2-100M</b:PartNumberFormatted>
</a:Attributes>
</a:CustomerPart>
<a:CustomerPart>
<a:Placement>
<a:GroupId>10</a:GroupId>
</a:Placement>
<a:Attributes xmlns:b="http://schemas.datacontract.org/2004/07/DealerBuilt.Models.Parts">
<b:Description>Kite SET, RR.</b:Description>
<b:PartNumber>60211T7J305ZZ</b:PartNumber>
</a:Attributes>
</a:CustomerPart>
</PullCustomerPartsPricingResult>
</PullCustomerPartsPricingResponse>
</s:Body>
</s:Envelope>
XSLT Code being applied Shown below:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<root xmlns="http://www.dataprint.com/global/3.0/rest/">
<xsl:for-each select="/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='PullCustomerPartsPricingResponse']/*[local-name()='PullCustomerPartsPricingResult']/*[local-name()='CustomerPart']">
<partDetail>
<partNumber>
<xsl:value-of select="/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='PullCustomerPartsPricingResponse']/*[local-name()='PullCustomerPartsPricingResult']/*[local-name()='CustomerPart']/*[local-name()='Attributes'] /*[local-name()='PartNumber']" />
</partNumber>
<partDescription>
<xsl:value-of select="/*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='PullCustomerPartsPricingResponse']/*[local-name()='PullCustomerPartsPricingResult']/*[local-name()='CustomerPart']/*[local-name()='Attributes'] /*[local-name()='Description']" />
</partDescription>
</partDetail>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
My Current sample Output Shown below:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://www.dataprint.com/global/3.0/rest/">
<results>
<partDetail>
<partNumber>31500SB2100M</partNumber>
<partDescription>PAD SET, RR.</partDescription>
</partDetail>
</results>
<results>
<partDetail>
<partNumber>31500SB2100M</partNumber>
<partDescription>PAD SET, RR.</partDescription>
</partDetail>
</results>
</root>
My Desired Output shown below:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://www.dataprint.com/global/3.0/rest/">
<results>
<partDetail>
<partNumber>31500SB2100M</partNumber>
<partDescription>PAD SET, RR.</partDescription>
</partDetail>
</results>
<results>
<partDetail>
<partNumber>60211T7J305ZZ</partNumber>
<partDescription>Kite SET, RR.</partDescription>
</partDetail>
</results>
</root>
I'm trying to loop element " a:CustomerPart" to print PartNumber and b:Descriptionas Output but first a:CustomerPart data is getting repeated Twice.I Undestand there is code change is required,please help on this.
Eiríkr Útlendi your patience/suggestions is much appreciated

Inside the xsl:for-each, the selected CustomerPart item becomes the context item. You should then select children/descendants of this CustomerPart using a relative path starting at this context item, not an absolute path starting at the root of the document (/).
That is, you should replace
<xsl:value-of select="/*[local-name()='Envelope']/*[local-name()='Body']
/*[local-name()='PullCustomerPartsPricingResponse']
/*[local-name()='PullCustomerPartsPricingResult']
/*[local-name()='CustomerPart']
/*[local-name()='Attributes']
/*[local-name()='PartNumber']" />
by
<xsl:value-of select="*[local-name()='Attributes'] /*[local-name()='PartNumber']" />
or better, by
<xsl:value-of select="*:Attributes/*:PartNumber" />

Related

XSLT 3.0 Streaming (Saxon) facing error "There is more than one consuming operand" when I use two different string functions within same template

Here is my sample input xml
<?xml version="1.0" encoding="UTF-8"?>
<Update xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Request>
<List>
<RequestP><ManNumber>3B4</ManNumber></RequestP>
<RequestP><ManNumber>8T7_BE</ManNumber></RequestP>
<RequestP><ManNumber>3B5</ManNumber></RequestP>
<RequestP><ManNumber>5E9_BE</ManNumber></RequestP>
<RequestP><ManNumber>9X6</ManNumber></RequestP>
</List>
</Request>
</Update>
and xslt
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" exclude-result-prefixes="#all">
<xsl:output method="xml" omit-xml-declaration="no" indent="yes" />
<xsl:mode streamable="yes" />
<xsl:template match="List/RequestP/ManNumber">
<ManNumber>
<xsl:value-of select="replace(.,'_BE','')" />
</ManNumber>
<xsl:if test="contains(.,'_BE')">
<ManDescrip>BE</ManDescrip>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
I am getting below error for above xslt, I am using Saxon 11.2 version
Template rule is not streamable
* There is more than one consuming operand: {<ManNumber {xsl:value-of}/>} on line 6, and {if(fn:contains(...)) then ... else ...} on line 9
The xslt works fine if I use either "replace" or "contains" but not both within same template.
Streamed processing, if you have needs (huge input documents in the size of gigabytes) to use it, requires you to limit your XSLT to streamable code, that means you can for instance make a copy of that element and processed only that small element node as a complete in memory element in a different mode
<xsl:template match="List/RequestP/ManNumber">
<xsl:apply-templates select="copy-of(.)" mode="grounded"/>
</xsl:template>
<xsl:template name="grounded" match="ManNumber">
<ManNumber>
<xsl:value-of select="replace(.,'_BE','')" />
</ManNumber>
<xsl:if test="contains(.,'_BE')">
<ManDescrip>BE</ManDescrip>
</xsl:if>
</xsl:template>

Creating xsl:result-document with xpath 3.1 fn:transform using saxon 9.9 EE

I'd like to create an output document using the xpath 3.1 fn:transform. Following is A.xsl. It creates A.xml when run directly (from oxygen):
<?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"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
exclude-result-prefixes="xs math"
version="3.0">
<xsl:output name="xml" method="xml" indent="true" />
<xsl:template name="xsl:initial-template">
<xsl:message select="'A'"/>
<xsl:result-document href="file:/C:/Work/test/A.xml" format="xml">
<resultDoc>
<text>The result of A.</text>
</resultDoc>
</xsl:result-document>
</xsl:template>
</xsl:stylesheet>
Result: A.xml is created with the desired output:
<?xml version="1.0" encoding="UTF-8"?>
<resultDoc>
<text>The result of A.</text>
</resultDoc>
Now, using the transform function to call A.xsl:
<?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="3.0">
<xsl:output name="xml" method="xml" encoding="UTF-8" indent="true" />
<!-- Global Constants -->
<xsl:variable name="xsl-file-base" select="'file:/C:/Work/test/'" as="xs:string"/>
<xsl:variable name="xsl-pipeline" select="'A.xsl'" as="xs:string"/>
<!-- Entry Point -->
<xsl:template name="xsl:initial-template">
<xsl:iterate select="$xsl-pipeline">
<xsl:variable name="file" select="$xsl-file-base || ." as="xs:string"/>
<xsl:result-document href="file:/C:/Work/test/A.xml" format="xml">
<xsl:sequence select="transform(map{'stylesheet-location' : $file})?output"/>
</xsl:result-document>
</xsl:iterate>
</xsl:template>
</xsl:stylesheet>
Result: A.xml is created but incomplete. Any help is appreciated.
<?xml version="1.0" encoding="UTF-8"?>
The result of the transform function is a map with an entry named output for the primary result document and further entries for secondary result documents. Your called stylesheet creates a secondary result with the URI file:/C:/Work/test/A.xml so
<xsl:sequence
select="transform(map{'stylesheet-location' : $file})('file:/C:/Work/test/A.xml')"/>
is more likely to produce an output.

Iterate over temporary document using xslt

I'm trying to create a temporary document containing some data, so I don't have it scattered all over the xsl file. I'm trying to loop through this data in the following manner:
<xsl:variable name="stuff">
<foo name="bar" key="83"/>
<foo name="baz" key="73"/>
<foo name="qux" key="71"/>
<foo name="quux" key="72"/>
</xsl:variable>
<xsl:for-each select="$stuff/foo" >
<xsl:value-of select="#key" />
</xsl:for-each>
The for-each block is never entered. I've tried to mimic the method described here. I also looked into using node-set(), but as far as I can tell that function is only required for XSLT version 1.0?
Tim C gave a complete answer about this in the following thread: Use a xsl:for-each over an xml to decide which label-value pairs are displayed?. I suggest you have a look at it.
Basically for XSLT 2.0 you can include the XML which you entered now into a <xsl:variable> directly into your XSLT:
<my:stuff>
<foo name="bar" key="83"/>
<foo name="baz" key="73"/>
<foo name="qux" key="71"/>
<foo name="quux" key="72"/>
</my:stuff>
Then in your XSLT where needed declare a variable:
<xsl:variable name="stuff" select="document('')/*/my:stuff" />
So the XSLT will be something like:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my" exclude-result-prefixes="my">
<my:stuff>
<foo name="bar" key="83"/>
<foo name="baz" key="73"/>
<foo name="qux" key="71"/>
<foo name="quux" key="72"/>
</my:stuff>
<xsl:template match="/">
<xsl:variable name="stuff" select="document('')/*/my:stuff"/>
<xsl:for-each select="$stuff/foo">
<xsl:value-of select="#key" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

exslt features not working in ruby-xslt gem

I have a Rails app and use Bundler to manage gems.
I use the ruby-xslt gem for XSLT transformations.
The gem is not recognizing exslt features like node-set().
Here’s the stylesheet I’ve been testing with
<xsl:stylesheet version="1.1"
xmlns="http://max.gov"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:pre="http://max.gov"
xmlns:exslt="http://exslt.org/common">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<!--The fabricated xml I want to access with exslt:node-set()-->
<xsl:variable name="PRE">
<root>
<item/>
<item/>
</root>
</xsl:variable>
<xsl:template match="/">
<results>
<run1 name="exslt:node-set($PRE)//pre:item">
<results>
<xsl:for-each select="exslt:node-set($PRE)//pre:item">
<success position="{position()}"/>
</xsl:for-each>
</results>
</run1>
<run2 name="exslt:node-set($PRE)//pre:item">
<results>
<xsl:for-each select="exslt:node-set($PRE)//pre:item">
<success position="{position()}"/>
</xsl:for-each>
</results>
</run2>
</results>
</xsl:template>
</xsl:stylesheet>
Here’s output I get when using XML::XSLT in our app:
<?xml version="1.0" encoding="UTF-8"?>
<results xmlns="http://max.gov" xmlns:pre="http://max.gov" xmlns:exslt="http://exslt.org/common">
<run1 name="exslt:node-set($PRE)//pre:item">
<results/>
</run1>
</results>
(Note that not only did the node-set function not work, the element got skipped altogether.)
The code is basically this:
xslt = XML::XSLT.new()
xslt.xml = #xml # #xml string
xslt.xsl = #record.xslt # xslt is stored in db
result = xslt.serve
render :xml => result
Here’s the expected output
<?xml version="1.0" encoding="UTF-8"?>
<results xmlns="http://max.gov" xmlns:pre="http://max.gov" xmlns:exslt="http://exslt.org/common">
<run1 name="exslt:node-set($PRE)//pre:item">
<results>
<success position="1"/>
<success position="2"/>
</results>
</run1>
<run2 name="exslt:node-set($PRE)//pre:item">
<results>
<success position="1"/>
<success position="2"/>
</results>
</run2>
</results>
I get this correct result when doing any of the following…
Run xsltproc from command line
Run the following in the Rails console
xslt = XML::XSLT.new
xslt.xml = File.read('data.xml')
xslt.xsl = File.read('test.xsl')
xslt.serve
We ended up replacing ruby-xslt with Nokogiri and this fixed our problems

xslt Ant Task not passing parameters to my stylesheet

I have a style sheet like this
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="testParam"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="resources/integer[#name='LOG_LEVEL']/text()">
<xsl:value-of select="$testParam"/>
</xsl:template>
</xsl:stylesheet>
And I have an input xml like this:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="LOG_LEVEL">3</integer>
<string name="app_name">Test Application</string>
</resources>
But when I try to call an xslt transform in ant using this:
<xslt in="in.xml" out="out.xml" style="style_above.xsl">
<outputproperty name="method" value="xml"/>
<outputproperty name="encoding" value="UTF-8"/>
<outputproperty name="indent" value="yes"/>
<param name="testParam" expression="test"/>
</xslt>
I get the following:
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<integer name="LOG_LEVEL"/>
<string name="app_name">Test Application</string>
</resources>
it doesn't seem to be changing my xslt parameter to the value I specify in my ant target
Yep, i figured out the problem was a different thing. Was about to update this was too late. I defined the xslt task in a macro and have a optional element also named param and that was the culprit. Thanks

Resources