XPath to Get Value from Sibling with Namespace - xslt

I have the following xml doc:
<GetGeneralServiceRequestByReferenceValueResponse xmlns="http://www.caps-solutions.co.uk/webservices/connectors/731/servicerequest/messagetypes">
<GeneralServiceRequest xmlns="http://www.caps-solutions.co.uk/schema/uniform/731/servicerequest/sr/srtypes">
<ServiceRequestIdentification>
<ServiceRequestTechnicalKey>PG7ECIJBKFX00</ServiceRequestTechnicalKey>
<ReferenceValue>18/009969/S_SCBC</ReferenceValue>
<AlternativeReferences>
<AlternativeReference xmlns="http://www.caps-solutions.co.uk/schema/uniform/72b/common/uniformtypes">
<ReferenceValue>W44811182451</ReferenceValue>
<ReferenceType>UTRN</ReferenceType>
</AlternativeReference>
<AlternativeReference xmlns="http://www.caps-solutions.co.uk/schema/uniform/72b/common/uniformtypes">
<ReferenceValue>00482</ReferenceValue>
<ReferenceType>BAD</ReferenceType>
</AlternativeReference>
</AlternativeReferences>
<SiteID>JB</SiteID>
</ServiceRequestIdentification>
</GeneralServiceRequest>
</GetGeneralServiceRequestByReferenceValueResponse>
I need to select the <ReferenceValue> that has a sibling <ReferenceType> of "UTRN"
The following xpath get's me the <ReferenceValue> of the last <Alternative> reference.
/*[local-name()='GetGeneralServiceRequestByReferenceValueResponse']/*[local-name()='GeneralServiceRequest']/*[local-name()='ServiceRequestIdentification']/*[local-name()='AlternativeReferences']/*[local-name()='AlternativeReference']/*[local-name()='ReferenceValue']
I've tried using [] for the parent AlternativeReference node then ReferenceValue='UTRN' but haven't been able to get the required output.

Don't use local-name(). Declare the namespaces in your XSLT and use the prefixes.
For example, declare them like this (you can pick any prefixes you like, as long as the namespace URIs match):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msg="http://www.caps-solutions.co.uk/webservices/connectors/731/servicerequest/messagetypes"
xmlns:sr="http://www.caps-solutions.co.uk/schema/uniform/731/servicerequest/sr/srtypes"
xmlns:uni="http://www.caps-solutions.co.uk/schema/uniform/72b/common/uniformtypes"
exclude-result-prefixes="msg sr uni"
>
And use (wrapped for legibility, but XPath is not whitespace-sensitive, you can wrap it the same way in your XSLT):
/msg:GetGeneralServiceRequestByReferenceValueResponse
/sr:GeneralServiceRequest
/sr:ServiceRequestIdentification
/sr:AlternativeReferences
/uni:AlternativeReference[uni:ReferenceType = 'UTRN']
/uni:ReferenceValue
Here, /uni:AlternativeReference[uni:ReferenceType = 'UTRN'] only selects those <AlternativeReference> nodes who have a <ReferenceType> child of the wanted value.
Since there are many ways to look at an XML tree (and because I think that explicitly. naming. every. single. step. along. the. way. is overdoing it), something like this would also work:
//uni:ReferenceType[. = 'UTRN']/../uni:ReferenceValue
or
//uni:AlternativeReference[uni:ReferenceType = 'UTRN']/uni:ReferenceValue

You can add criteria to the <AlternativeReference> predicate to restrict the selection of <AlternativeReference> elements to those that have a <ReferenceType> element who's value is "UTRN":
/*[local-name()='GetGeneralServiceRequestByReferenceValueResponse']/
*[local-name()='GeneralServiceRequest']/
*[local-name()='ServiceRequestIdentification']/
*[local-name()='AlternativeReferences']/
*[local-name()='AlternativeReference' and *[local-name()='ReferenceType' and .='UTRN']]/
*[local-name()='ReferenceValue']

This should do:
"//AlternativeReference[1]/ReferenceValue"
Or if you know the sibling value:
"//ReferenceType[text()='UTRN']/parent::*/ReferenceValue"

Related

How to transverse xml nodes using xslt to match values in form of where condition

The output below is expected. Given the xslt is sorted by SHIPPEDQTYPALLETNR
The Output xml below
<ORDER_DATA_Main>
<ORDER_DATA>
<SHIPPEDQTYPALLETNR>97915</SHIPPEDQTYPALLETNR>
<MASTER_P>0001</MASTER_P>
</ORDER_DATA>
<ORDER_DATA>
<SHIPPEDQTYPALLETNR>97916</SHIPPEDQTYPALLETNR>
<MASTER_P>0002</MASTER_P>
</ORDER_DATA>
<ORDER_DATA>
<SHIPPEDQTYPALLETNR>97917</SHIPPEDQTYPALLETNR>
<MASTER_P>0003</MASTER_P>
</ORDER_DATA>
</ORDER_DATA_Main>
xml file
<ORDER_DATA>
<ORDER_P>
<ORDER_QUANTITY>
<SKU_INFO>
<TQTY>260</TQTY>
</SKU_INFO>
<SHIPPED_GOODS_INFO_HEADER>
<SHIPPED_GOODS_INFO>
<CONTRYOFORIGIN>US</CONTRYOFORIGIN>
<SHIPPEDQTYPALLETNR>97916</SHIPPEDQTYPALLETNR>
</SHIPPED_GOODS_INFO>
<MASTER_PALETTE>
<MASTER_P>0002</MASTER_P>
</MASTER_PALETTE>
</SHIPPED_GOODS_INFO_HEADER>
</ORDER_QUANTITY>
</ORDER_P>
<ORDER_P>
<ORDER_QUANTITY>
<SKU_INFO>
<TQTY>250</TQTY>
</SKU_INFO>
<SHIPPED_GOODS_INFO_HEADER>
<SHIPPED_GOODS_INFO>
<CONTRYOFORIGIN>US</CONTRYOFORIGIN>
<SHIPPEDQTYPALLETNR>97915</SHIPPEDQTYPALLETNR>
</SHIPPED_GOODS_INFO>
<MASTER_PALETTE>
<MASTER_P>0001</MASTER_P>
</MASTER_PALETTE>
</SHIPPED_GOODS_INFO_HEADER>
</ORDER_QUANTITY>
</ORDER_P>
</ORDER_DATA>
below xslt snippet
xsltsnippet
xsltcode
<MASTER_P>
<xsl:value-of select="//MASTER_PALETTE[//SHIPPED_GOODS_INFO/SHIPPEDQTYPALLETNR=$v_pallett]/MASTER_P"/>
</MASTER_P>
MASTER_PALETTE and SHIPPED_GOODS_INFO are siblings. When you write
select="//MASTER_PALETTE[//SHIPPED_GOODS_INFO...]
the // within the predicate means the predicate will be true if any SHIPPED_GOODS_INFO anywhere in the document satisfies the conditions, regardless whether it is related in any way to the MASTER_PALETTE being tested. Instead of // here, try ../.

Attribute Comparison giving false matches and non matches in tinyxpath

I am using tinyxpath-1.3.1. C/C++ on Linux. When I do a xpath search on a document I am not finding nodes when I think I should.
My XML:
<data>
<event deviceId="25479545.5" interface="sensor-multilevel"
command="state" label="luminance" newValue="800"
oldValue="9" time="1412227484" />
</data>
My xpath Expression:
/data/event[#deviceId="25479545.5" and #interface="sensor-multilevel" and
#label="luminance" and #newValue>600 and #oldValue<10]
If I take the oldValue out and use () like this the comparison works:
/data/event[(#deviceId="25479545.5" and #interface="sensor-multilevel") and
(#label="luminance" and #newValue>600)]
Is there some limit on the number of comparisons?
Anything special with converting the 600 to a decimal?
Do I need to "" the value 600, it seems to work either way.
Any ideas on how to get it to work with the oldValue attribute included in the expression?
TinyXPath call:
TiXmlNode * node = TinyXPath::XNp_xpath_node( root, expression.c_str() );
Thanks
Larry
You seem to be getting a string comparison here rather than a numeric comparison ("9" > "10" in alphabetic order). That's incorrect according to the specs (both XPath 1.0 and 2.0, though they get there in rather different ways).
The safest approach is probably to convert explicitly to a number: write
number(#oldValue) < 10
Looks like the tinyxpath library has some bugs....
This works for tinyxpath-1.3.1:
/data/event[((#deviceId="25479545.5" and #interface="sensor-multilevel") and
(#label="luminance" and #newValue>"600")) and (#oldValue<"10")]
parenthesis were needed to group these in a final set of 2.
Or I just convert to using libxml2.

How to use variables defined with assign in list elements in freemarker

i have a freemarker problem. I have one hash map called nodes and i iterate trough it like this:
<#list hashmap.collection as nodes>
.....some displaying
<#assign nodeName>
${nodes.name}
</#assign>
<#list hashmap2.nodeName.collection as nodes2>
.......some more displaying
And this code is not because freemarker is trying to find nodeName key inside the hashmap2...
Is there a way to do this in freemarker?
Thanks for your answers!
That should be hasmap2[nodeName].collection. What you put after . is always seen as literally the sub-variable name, while inside [] you can give an arbitrary expression as far as it evaluates to a string. Thus, you may don't need that #assign at all, and you could write hashmap2[nodes.name].collection.
Also, instead of <#assign nodeName>${nodes.name}</#assign> you should just write <#assign nodeName = nodes.name>. Again, if you need the assignment at all.
Also since nodes store a single node, your code would be more readable if you call it node.

XSLT to get value of node in next loop

Below is the xml. Now I am looking for an xslt where in the first loop of <ns0:EBLoop1> when EB01 = 1 then I need to get the value of <EB05>PACKAGE A STANDARD PLAN</EB05> in the next EBLoop1.
How can I do this.
<ns0:EBLoop1>
<ns0:EB>
<EB01>1</EB01>
<EB05>This</EB05>
<EB07>0</EB07>
</ns0:EB>
<ns0:MSG>
<MSG01>Please See the Provider Manual</MSG01>
</ns0:MSG>
</ns0:EBLoop1>
<ns0:EBLoop1>
<ns0:EB>
<EB01>D</EB01>
<EB05>PACKAGE A STANDARD PLAN</EB05>
<EB07>0</EB07>
</ns0:EB>
</ns0:EBLoop1>
<ns0:EBLoop1>
<ns0:EB>
<EB01>F</EB01>
<EB03>30</EB03>
<EB07>0</EB07>
</ns0:EB>
</ns0:EBLoop1>
Thanks,
Gopi
Assuming the context node is your EBLoop1 element, you can get the next one with xpath:
../following-sibling::ns:EBLoop1[1]/ns:EB/EB05
But you need to define your namespace and make use of the prefix in your xpath.

XSLT 1 - find child node where node has case-insensitive value

I have the following selector, which works:
parent::node()/myNS:expField[myNS:Nam='NAMETOFIND']/myNS:Val
What I want is to do case-insensitive matching on the myNS:Nam value, so I would be able to select <Val> from any of the following:
<expField>
<Nam>NAMETOFIND</Nam>
<Val>the value I want</Val>
</expField>
<expField>
<Nam>NameToFind</Nam>
<Val>the value I want</Val>
</expField>
<expField>
<Nam>nametofind</Nam>
<Val>the value I want</Val>
</expField>
<expField>
<Nam>nAmEtOFInD</Nam>
<Val>the value I want</Val>
</expField>
I'm using XSLT 1, so I can't use lower-case().
translate() will do the job, it's not pretty but it works. If you know what language you want to process, that is.