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

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.

Related

Accessing the attribute of a child node through a variable

I am using XLS through C# and I need to be able to access the attributes of a specific node, but whatever I do I get the same "eof with #" error. Am I doing something wrong? Is there something I am missing? This is a code sample:
<xsl:value-of select="$main[1]#index"/>
Error message:
System.Xml.Xsl.XslLoadException: 'Expected end of the expression, found '#'.
$main[1] -->#<-- index'
To select an attribute named index of the context element use #index. Use that in a separate step if you first select elements e.g. foo/#index selects the index attributes of all foo children of the context node.
$main[0] doesn't make much sense as in XPath the first item has the index 1 so perhaps $main[1]/#index is what you want, it depends on how the variable or parameter main has been bound to a value.

XPath to Get Value from Sibling with Namespace

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"

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.

Construct Xpath

I have the following repeated piece of the web-page:
<div class="txt ext">
<strong class="param">param_value1</strong>
<strong class="param">param_value2</strong>
</div>
I would like to extract separately values param_value1 and param_value2 using Xpath. How can I do it?
I have tried the following constructions:
'//strong[#class="param"]/text()[0]'
'//strong[#class="txt ext"]/strong[#class="param"][0]/text()'
'//strong[#class="param"]'
none of which returned me separately param_value1 and param_value2.
P.S. I am using Python 2.7 and the latest version of Scrapy.
Here is my testing code:
test_content = '<div class="txt ext"><strong class="param">param_value1</strong><strong class="param">param_value2</strong></div>'
sel = HtmlXPathSelector(text=test_content)
sel.select('//div/strong[#class="param"]/text()').extract()[0]
sel.select('//div/strong[#class="param"]/text()').extract()[1]
// means descendant or self. You are selecting any strong element in any context. [...] is a predicate which restricts your selection according to some boolean test. There is no strong element with a class attribute which equals txt ext, so you can exclude your second expression.
Your last expression will actually return a node-set of all the strong elements which have a param attribute. You can then extract individual nodes from the node set (use [1], [2]) and then get their text contents (use text()).
Your first expression selects the text contents of both nodes but it's also wrong. It's in the wrong place and you can't select node zero (it doesn't exist). If you want the text contents of the first node you should use:
//strong[#class="param"][1]/text()
and you can use
//strong[#class="param"][2]/text()
for the second text.

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.