xquery/xslt for selecting a value based on a specific value - xslt

I am stuck on an xquery for below request. I want to select the address based on the type value.
i.e. if type is "StreetAddress" then pick freeFormat "Maguire"
<associatedAddress>
<address xmlns="http://services.oracle.com/v1.0/Common">
<type xmlns="http://services.oracle.com/v1.0/Common">StreetAddress</type>
<freeFormat xmlns="http://services.oracle.com/v1.0/Common">Maguire</freeFormat>
</address>
<address>
<type xmlns="http://services.oracle.com/v1.0/Common">CityAddress</type>
<freeFormat xmlns="http://services.oracle.com/v1.0/Common">SanFransisco</freeFormat>
</address>
</associatedAddress>
I tried in couple of ways:
if associatedAddress/address/type="StreetAddress"
then.., But this one gives me only the first address
I also tried to use a for loop and then use if case inside it but even that gave me just the first address
Please let me know any other options. Thanks

The XPath for fetching value Maguire using the value in <type> is
<xsl:value-of select="associatedAddress/address[type='StreetAddress']/freeFormat" />
You need to take care of the namespace associated with the different elements in your input XML when fetching the value. If the namespace is not properly handled in the XSL, the value will not be extracted.

Since you asked for xQuery given your input xml, this:
declare namespace common="http://services.oracle.com/v1.0/Common";
for $n in associatedAddress//common:type[. = "StreetAddress"]
return
$n/../common:freeFormat
produces the desired result
<freeFormat xmlns="http://services.oracle.com/v1.0/Common">Maguire</freeFormat>
The trick is that in your input xml associatedAddress is not in the same namespace as the other elements, so you need to adjust your query to take this into account.
You mention an if clause, but its is not clear what the desired output is from your question. You can use the below as a template to modify results.
declare namespace common="http://services.oracle.com/v1.0/Common";
for $n in associatedAddress//common:type
return
switch($n)
case "StreetAddress" return $n/../common:freeFormat
case "CityAddress" return "something else"
default
return ()

Related

Using XSLT to transform XML to C++ initializer list

I've been trying to use XSLT to transform an XML into C++ code. I have fields that represent class member variables, with information like their type. For example:
<members>
<member name="Attribute1" type="int" length="32" />
<member name="Attribute2" type="string" length="64" />
<member name="Attribute3" type="char array" length="10" />
<member name="Attribute4" type="unsigned int" length="32" />
</members>
However, I really can't seem to come up with any way to write a proper initialisation list for constructors. My main problem comes from the C++ syntax:
ClassName:ClassName()
: Attribute1(value),
Attribute2(value),
Attribute4(value)
{
memset(Attribute3, 0, typeof(Attribute));
}
The part that is giving me trouble is the fact that the last member initialized is not followed by a comma. This is should be an easy problem, but XSLT's functional + declarative nature is giving me hell. Here is what I have considered so far:
1) Create a printInitialisationList template which has a "firstMemberPrinted" param. If this param is false, we print a comma and a newline first. If it is true (meaning this is the first member being printed), we don't print the comma. The problem with this, however, is that since variables are immutable in XSLT, I can't actually create a variable to pass in and change the value once the I have printed a member.
2) I thought about solving #1 by simply checking if a "printed" variable had already been created, and if not, to pass True into firstMemberPrinted and then create the variable. However, since XSLT is a declarative language, the idea of checking for an undeclared variable doesn't actually make sense.
3) Checking if "position() = last" doesn't help, since the last member could very well just be an array, which needs to be initialized inside the braces.
Note that I can't change the format of the XML file :(
I would appreciate all and any help! Thanks!

How to use contains() with a set of strings in XSLT

I have the following XML snippet:
<figure customer="ABC DEF">
<image customer="ABC"/>
<image customer="XYZ"/>
</figure>
I'd like to check if the figure element's customer attribute contains the customer attributes of the image elements.
<xsl:if test="contains(#customer, image/#customer)">
...
</xsl:if>
I get an error saying:
a sequence of more than one item is not allowed as the second argument of contains
It's important to note that I cannot tell the values of the customer attributes in advance, thus using xsl:choose is not an option here.
Is it possible to solve this without using xsl:for-each?
In XSLT 2.0 you can use:
test="image/#customer/contains(../../#customer, .) = true()"
and you will get a true() result if any of them are true. Actually, that leads me to suggest:
test="some $cust in image/#customer satisfies contains(#customer, $cust)"
but that won't address the situation where the customer string is a subset of another customer string.
Therefore, perhaps this is best:
test="tokenize(#customer,'\s+') = image/#customer"
... as that will do a string-by-string comparison and give you true() if any of the tokenized values of the figure attribute is equal to one of the image attributes.

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

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.

xsl return the dynamic node value

hello:
do you guys know how to display the nodes' value which the nodes name are dynamic, for example, the nodes name is like x1, x2, x3... the number 1, 2 ,3 depends on the returns of the table.
i can get the node name using the loop, but only can get the name, even xsl:value-of select="$nodename", returns the nodename, not the value
As #Dimitre said, you haven't given us much specific information to work with, but in general you can use this to select elements whose names are determined at run time:
<xsl:value-of select="*[local-name() = $someDynamicValue]" />
You can also use name(), but local-name() ignores the namespace prefix, which usually makes things easier.
If you'd like more detailed help, please provide your sample input XML (especially the "returns of the table"), and the XSLT you've tried so far; and preferably, a sample of desired output XML.

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.