For-each loop in JDeveloper doesn't map the response at all - xslt

I'm developing a BPEL service using JDeveloper 11.1.1.5.0.
The API's response contains some recurring fields and I'm trying to use a for-each loop to map them with the final web-service's response parameters.
The API's response structure is like this :
<Data>
<Item>
<F6181_SubsWalletCounter>-1</F6181_SubsWalletCounter>
<FBalanceExpDate>2013-08-13T00:00:00</FBalanceExpDate>
<FResetWalletCounterValue>0</FResetWalletCounterValue>
<FRecurringPeriod>0</FRecurringPeriod>
<FRecurringRefreshDate>1899-12-30T00:00:00</FRecurringRefreshDate>
<FRecurringRefreshDay>0</FRecurringRefreshDay>
<F6150_AccountProfileId>18</F6150_AccountProfileId>
<FLimit>0</FLimit>
<F8345_PaymentDebt>0</F8345_PaymentDebt>
<F9217_MinBalance>0</F9217_MinBalance>
<F9218_MaxPaymentDebt>-1</F9218_MaxPaymentDebt>
</Item>
<Item>
<F6181_SubsWalletCounter>-1</F6181_SubsWalletCounter>
<FBalanceExpDate>2013-08-13T00:00:00</FBalanceExpDate>
<FResetWalletCounterValue>0</FResetWalletCounterValue>
<FRecurringPeriod>0</FRecurringPeriod>
<FRecurringRefreshDate>1899-12-30T00:00:00</FRecurringRefreshDate>
<FRecurringRefreshDay>0</FRecurringRefreshDay>
<F6150_AccountProfileId>18</F6150_AccountProfileId>
<FLimit>0</FLimit>
<F8345_PaymentDebt>0</F8345_PaymentDebt>
<F9217_MinBalance>0</F9217_MinBalance>
<F9218_MaxPaymentDebt>-1</F9218_MaxPaymentDebt>
</Item>
</Data>
The <Item> element and its sub-elements could repeat multiple times maintaining the exact structure within itself. Out of these sub elements, few are to be mapped with the final response parameters.
The xsl code is :
<ns2:responseBody>
<ns2:balanceInfo>
<ns2:balance>
<xsl:for-each select=
"/tns:Subscriber_WalletInfo_GetResponse/Data/Item">
<ns2:wallet>
<xsl:value-of select="F6091_WalletTypeName"/>
</ns2:wallet>
</xsl:for-each>
<ns2:expirationDate>
<xsl:value-of select="FBalanceExpDate"/>
</ns2:expirationDate>
<ns2:balanceAmount>
<xsl:value-of select="F9261_Balance"/>
</ns2:balanceAmount>
<ns2:unit>
<xsl:value-of select="F8341_CurrencyName"/>
</ns2:unit>
</ns2:balance>
</ns2:balanceInfo>
<ns3:Error>
<ns3:description>
<xsl:value-of select="/tns:Subscriber_WalletInfo_GetResponse/ErrorDescription"/>
</ns3:description>
</ns3:Error>
</ns2:responseBody>
But the response doesn't get mapped with this xsl. I'm not sure if I've made any syntactical error or if any namespace is missing.
Any kind of guidance would be great!

I'm not sure if I've made any syntactical error or if any namespace is missing.
Right both times, I think.
First, the syntax errors.
If the XML introduced with the words "The xsl code is" is in fact the entire stylesheet, then your first syntactic error is that your stylesheet is not namespace-well-formed: you have not declared any of the namespace prefixs ns2, ns3, xsl.
Your second syntactic error is at the XSLT level: the namespace prefix tns is also not declared.
Your third syntactic error is that the outermost element does not include any xsl:version attribute.
When I supply dummy namespace declarations for the unbound prefixes (and http://www.w3.org/1999/XSL/Transform for the prefix xsl) and supply an xsl:version="1.0" attribute on the outermost element, then the stylesheet becomes a legal XSLT 1.0 stylesheet, using the "literal result element as stylesheet" option.
So much for the syntax errors. When I run the repaired stylesheet on the input you supply, I get output. Not the output you want, but it's progress:
<?xml version="1.0"?>
<ns2:responseBody xmlns:ns2="http://example.com/ns2"
xmlns:ns3="http://example.com/ns3"
xmlns:tns="http://example.com/tns">
<ns2:balanceInfo>
<ns2:balance>
<ns2:expirationDate/>
<ns2:balanceAmount/>
<ns2:unit/>
</ns2:balance>
</ns2:balanceInfo>
<ns3:Error>
<ns3:description/>
</ns3:Error>
</ns2:responseBody>
Now the semantic errors, which include but are not limited to a namespace error.
You're not getting any information about your input here, because the select expressions in your stylesheet (specifically /tns:Subscriber_WalletInfo_GetResponse/Data/Item and /tns:Subscriber_WalletInfo_GetResponse/ErrorDescription) aren't matching anything. Both of these path expressions say, roughly:
Start at the root of the document (that's the /).
The outermost element will be named Subscriber_WalletInfo_GetResponse, in the namespace bound to prefix tns.
Within that element, the second select expression searches for a child named ErrorDescription which is not namespace-qualified. The first expression searches for a child (or: the set of all children) named Data, and then within the Data element(s), the set of all children named Item, and evaluates the contents of the for-each instruction once for each of them. The elements Item and Data are both expected to be namespace-unqualified.
In the data you show, by contrast, the outermost element is not named Subscriber_WalletInfo_GetResponse and is not namespace-qualified. Naturally, neither select expression doesn't match anything: they both fail at step 2.

Related

Consuming the same node twice in Streaming XSLT

I am trying to convert some XML to the interstitial JSON representation specified in XSLT 3.0 (for later conversion to JSON via xml-to-json). My current XSLT works well as a non-streaming stylesheet, but I'm running into issues during the conversion to streaming. Specifically, there are situations where I need to consume the same node twice, especially in the case of repeating tags in XML, which I am converting to arrays in equivalent JSON representation.
<xsl:if test="boolean(cdf:AdjudicatorName)">
<array key="AdjudicatorName">
<xsl:for-each select="cdf:AdjudicatorName">
<string>
<xsl:value-of select="."/>
</string>
</xsl:for-each>
</array>
</xsl:if>
boolean(cdf:AdjudicatorName) tests for the existance of the tag, and creates an array if so.
This code fails in Oxygen (Saxon-EE) with the following message
Template rule is declared streamable but it does not satisfy the streamability rules. * There is more than one consuming operand: {fn:exists(...)} on line {X}, and {<string {(attr{key=...}, ...)}/>} on line {Y}
I am aware of the copy-of workaround, however, many items in the source file can repeat at the highest level, so use of this approach would yield minimal memory savings.
This looks like the perfect use case for xsl:where-populated:
<xsl:where-populated>
<array key="AdjudicatorName">
<xsl:for-each select="cdf:AdjudicatorName">
<string>
<xsl:value-of select="."/>
</string>
</xsl:for-each>
</array>
</xsl:where-populated>
The xsl:where-populated instruction (which was invented for exactly this purpose) logically evaluates its child instructions, and then eliminates any items in the resulting sequence that are "deemed empty", where an element is deemed empty if it has no children. In a streaming implementation the start tag (<array>) will be "held back" until its first child is generated, and when the corresponding end tag (</array>) is emitted, the pair of tags will be discarded if there were no intervening children emitted.

XSLT 2.0 using key with except returns unexpected result

NB: title changed to reflect the problem better.
My xml documents contain an element <tei:seg #type #xml:id #corresp> which wrap little 'stories'. The attribute #corresp allows me to connect these stories to a master story. For example, these seg are all connected by their #corresp:
doc1.xml//seg[#type='dep_event' #corresp='#JKL' #xml:id='doc1-05']
doc2.xml//seg[#type='dep_event' #corresp='#JKL' #xml:id='doc2-06']
doc6.xml//seg[#type='dep_event' #corresp='#JKL' #xml:id='doc6-03']
My objective is: when the XSLT template finds a #corresp, find other seg in other documents with the same #corresp and output their respective `#xml:id``
So, in the above example, if the current seg was #xml:id='doc1-05', the template outputs a list: Corresponds to doc2-06, doc6-03
Until I can solve the current problems with XSLT collection() in eXist-DB, I'm falling back on my previous solution: a 'TEI corpus' xml document which maintains a master list of all related tei-xml documents via xi:include. This way I provide a single document node whereby the processor can access and search all the xml documents.
So, I declare the corpus document:
<xsl:variable name="corpus" select="doc('ms609_corpus.xml')"/>
Then create a key for the #corresp:
<xsl:key name="correspkey" match="//tei:seg[#type='dep_event' and #corresp]" use="#corresp"/>
Then I use the key with the doc() to search:
<xsl:when test="tei:seg[#type='dep_event' and #corresp]">
<xsl:variable name="correspvar"
select="data(self::seg[#type='dep_event' and #corresp]/#corresp)"/>
<xsl:text>Corresponds to </xsl:text>
<xsl:value-of select="data($corpus/(key('correspkey',$correspvar) except $correspvar)/#xml:id)" separator=", "/>
</xsl:when>
It returns the results, but the except should exclude the current #corresp. Yet it is included in the results.
The except operator works on sequences of nodes based on node identity, see https://www.w3.org/TR/xpath20/#combining_seq defining
The except operator takes two node sequences as operands and returns a
sequence containing all the nodes that occur in the first operand but
not in the second operand ... All these operators eliminate duplicate
nodes from their result sequences based on node identity
Based on that I think you simply want
<xsl:value-of select="$corpus/(key('correspkey', current()/#corresp) except current())/#xml:id)" separator=", "/>
Using data on nodes which atomizes nodes to values and then trying to use except which works on nodes doesn't seem to make sense to me.

pre-processing script to switch product codes

I have a snippet of code I've inherited and I'm trying to get it to work on multiples of the match pattern and set a tag from looking up a value from a table using another tag. What happens is that, for every item, the same lookup is performed and not the relative one for the node. I can't work out the syntax to work thru all entries and substitute the correct one. It's got to be simple it's just that I am simpler :)
My source xml contains this (within an outer /oomsdoc document node not shown):
<item>
<lineqty> 1</lineqty>
<linesku>BNLP5008 </linesku>
<linecustprod>xxxxxxxxxxxxxxx</linecustprod>
<linedesc>London Pride (Bot500mlx8) </linedesc>
</item>
<item>
<lineqty> 1</lineqty>
<linesku>BNBL5008 </linesku>
<linecustprod>xxxxxxxxxxxxxxx</linecustprod>
<linedesc>Bengal Lancer (Bot500mlx8) </linedesc>
</item>
I want to substitute the xxxxxxxxxxxxxxx in each linecustprod tag with the material from the lookup table using the value of the linesku tag.
This is my lookup table:
<Materials>
<product sku='BNLP5008 ' material='LONDON PRIDE'/>
<product sku='BNBL5008 ' material='BENGAL LANCER'/>
</Materials>
and this is my xslt code.
<xsl:variable name="SkuList" select="document('d:\test\transforms\catalogue.xml')/Materials"/>
<xsl:template match="/oomsdoc/item/linecustprod">
<xsl:variable name="MySku" select="/oomsdoc/item/linesku"/>
<linecustprod>
<xsl:value-of select="$SkuList/product[#sku=$MySku]/#material"/>
</linecustprod>
</xsl:template>
I'm guessing some kind of xsl foreach would work but just can't find a usable example to crib :)
Your guidance again would be appreciated at this point in my frustration :)
Thanks,
Brian.
Changing the variable definition to
<xsl:variable name="MySku" select="../linesku"/>
should be sufficient, this will pull out the linesku that is a sibling to the linecustprod you're currently looking at. As currently defined the variable will contain a node set of all the linesku elements in the document, so the value-of will give you the first entry from $SkuList that matches any entry in the main input file.
In addition to Ian Roberts' answer, please change
<xsl:variable name="SkuList" select="document('d:\test\transforms\catalogue.xml')/Materials"/>
to
<xsl:variable name="SkuList" select="document('/d:\test\transforms\catalogue.xml')/Materials"/>
for some reason, the first throws an error (malformed URL).

Assigning parameter value to the xsl: for each

Can anybody who has worked with XSLT help me on this?
I am using XSL version 1.0.
I have declared a parameter in XSL file like:
<xsl:param name="HDISageHelpPath"/>
Now I am assigning the value to this parameter from an asp page . The value which I assign is "document('../ChannelData/Sage/help/ic/xml/HDI.xml')/HelpFiles/Help". Now I want to assign this parameter to the <xsl for each> like
<xsl:for-each select="msxsl:node-set($HDISageHelpPath)" > (This does not work)
But it does not work. I checked the parameter value by debugging it as below
<debug tree="$HDISageHelpPath">
<xsl:copy-of select="$HDISageHelpPath"/>
</debug>
I'm able to print the value and it seems correct. In fact when I assign the static path ("document('../ChannelData/Sage/help/ic/xml/HDI.xml')/HelpFiles/Help") by hard-coding it, it works
<xsl:for-each select="document('../ChannelData/Sage/help/ic/xml/HDI.xml')/HelpFiles/Help"> (This works)
Can anyone please let me know why assigning the parameter to xsl:for-each does not work?
Note: I have referred the site "http://www.dpawson.co.uk/xsl/sect2/N1553.html"
You can't easily evaluate dynamic strings as XPath expressions in XSLT 1.0. They must be hard-coded, normally.
There's EXSLT's dyn:evaluate(), but I doubt you can use that with the MXSML processor.
As an alternative approach, you could either try passing the file path only:
<xsl:param name="HDISageHelpFilePath"/>
<!-- ... -->
<xsl:for-each select="document($HDISageHelpFilePath)/HelpFiles/Help">
</xsl:for-each>
or making placeholder, replacing it with search-and-replace before you load the actual XSL code into the processor (as a string). This is a bit messy and error-prone, but it could give you the possibility to use an actual dynamic XPath expression.
<xsl:for-each select="%HELP_FILE_XPATH%">
</xsl:for-each>
Load the file as text, replace %HELP_FILE_XPATH% with your actual XPath, feed it to the processor. If it loads, you are fine, if it doesn't, your input XPath was malformed.

Ignore name space with t: prefix

We have XML file like below...
<?xml version='1.0'?>
<T0020 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.safersys.org/namespaces/T0020V1 T0020V1.xsd"
xmlns="http://www.safersys.org/namespaces/T0020V1">
<IRP_ACCOUNT>
<IRP_CARRIER_ID_NUMBER>1213561</IRP_CARRIER_ID_NUMBER>
<IRP_BASE_COUNTRY>US</IRP_BASE_COUNTRY>
<IRP_BASE_STATE>AL</IRP_BASE_STATE>
<IRP_ACCOUNT_NUMBER>15485</IRP_ACCOUNT_NUMBER>
<IRP_ACCOUNT_TYPE>I</IRP_ACCOUNT_TYPE>
<IRP_STATUS_CODE>0</IRP_STATUS_CODE>
<IRP_STATUS_DATE>2004-02-23</IRP_STATUS_DATE>
<IRP_UPDATE_DATE>2007-03-09</IRP_UPDATE_DATE>
<IRP_NAME>
<NAME_TYPE>LG</NAME_TYPE>
<NAME>WILLIAMS TODD</NAME>
<IRP_ADDRESS>
<ADDRESS_TYPE>MA</ADDRESS_TYPE>
<STREET_LINE_1>P O BOX 1210</STREET_LINE_1>
<STREET_LINE_2/>
<CITY>MARION</CITY>
<STATE>AL</STATE>
<ZIP_CODE>36756</ZIP_CODE>
<COUNTY/>
<COLONIA/>
<COUNTRY>US</COUNTRY>
</IRP_ADDRESS>
</IRP_NAME>
</IRP_ACCOUNT>
</T0020>
In order to Insert this XML data to database ,we have used two XSLT.
First XSLT will remove name space from XML file and convert this XML to some intermediate
XML(say Process.xml) file on some temporary location.
then we were taking that intermediate xml(without namespace lines) and applied another XSL
to map xml field to Database.
Then we have found solution and we have used only one XSLT which does bode [1] Remove namespace and [2] Mapping XML field to Database to insert data.
Our final style sheet contain following lines
xmlns:t="http://www.safersys.org/namespaces/T0020V1">
and we used following to map field to Database
<xsl:template match="/">
<xsl:element name="T0020">
<xsl:apply-templates select="t:T0020/t:IRP_ACCOUNT" />
</xsl:element>
</xsl:template>
how did our problem solved with this approach ?Any consequences with using this ?
I have searched about this but not getting the functionality.
Thanks in Advance..
I don't see any problems with your approach.
XSLT mandates a fully qualified name for a correct matching, so using a prefixed namespace in your XSLT is the right solution; this is why you solved your problem.