I am currently constructing an XPath condition in SAP PI (receiver determination object) which should either route the message to receiver 1 or receiver 2.
The given documentID values that the business sends are as follows.
Receiver 1 receives messages within below documentID range
Range: "F00" to "F99"
Receiver 2 receives messages within below documentID range
Range: "FA0" to "FZ9"
Sample condition that I can think of, but not sure if this will work or if the logic is correct. Follow up question too, does greater/less than signs accept non-numerical characters?
Condition for Receiver 1
(/p1:Upload/ContainerEvent[WorkAssignmentID >= F00] EX AND /p1:Upload/ContainerEvent[WorkAssignmentID <= F99] EX )
Condition for Receiver 2
(/p1:Upload/ContainerEvent[WorkAssignmentID >= FA0] EX AND /p1:Upload/ContainerEvent[WorkAssignmentID <= FZ9] EX )
I am also thinking if substring can be used in XPath. Feel free to provide your inputs. Thanks
Regards,
Charles Tan
Pure XPath 1.0 solution:
Receiver 1 receives messages within below documentID range Range: F00
to F99
/*/Upload/ContainerEvent
[WorkAssignmentId
[string-length() = 3]
[starts-with(., 'F')][substring(.,2,2) >= 0][99 >= substring(.,2,2)]
]/EX
Receiver 2 receives messages within below documentID range Range: FA0
to FZ9
/*/Upload/ContainerEvent
[WorkAssignmentId
[string-length() = 3]
[starts-with(., 'F')]
[26 > string-length(
translate('ABCDEFGHIJKLMNOPQRSTUVWXYZ',substring(.,2,1), ''))]
[substring(.,3,1) >= 0][9 >= substring(.,3,1)]
]/EX
Here is XSLT - based verification:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:copy-of select=
"/*/Upload/ContainerEvent
[WorkAssignmentId
[string-length() = 3]
[starts-with(., 'F')][substring(.,2,2) >= 0][99 >= substring(.,2,2)]
]/EX"/>
==============================
<xsl:copy-of select=
"/*/Upload/ContainerEvent
[WorkAssignmentId
[string-length() = 3]
[starts-with(., 'F')]
[26 > string-length(
translate('ABCDEFGHIJKLMNOPQRSTUVWXYZ',substring(.,2,1), ''))]
[substring(.,3,1) >= 0][9 >= substring(.,3,1)]
]/EX"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML source document (none provided):
<p1>
<Upload>
<ContainerEvent>
<WorkAssignmentId>F13</WorkAssignmentId>
<EX>F13</EX>
</ContainerEvent>
<ContainerEvent>
<WorkAssignmentId>F99</WorkAssignmentId>
<EX>F99</EX>
</ContainerEvent>
<ContainerEvent>
<WorkAssignmentId>E15</WorkAssignmentId>
<EX>E15</EX>
</ContainerEvent>
<ContainerEvent>
<WorkAssignmentId>FA7</WorkAssignmentId>
<EX>FA7</EX>
</ContainerEvent>
<ContainerEvent>
<WorkAssignmentId>FZ9</WorkAssignmentId>
<EX>FZ9</EX>
</ContainerEvent>
<ContainerEvent>
<WorkAssignmentId>FAB</WorkAssignmentId>
<EX>FAB</EX>
</ContainerEvent>
</Upload>
</p1>
The wanted result is produced:
<EX>F13</EX>
<EX>F99</EX>
==============================
<EX>FA7</EX>
<EX>FZ9</EX>
Answer to your follow-up question ("does greater/less than signs accept non-numerical characters?"): in XPath 1.0, no, greater-than/less-than operate only on numerics. This changes in XPath 2.0.
Related
I'm using a European Space Agency API to query (result can be viewed here) for satellite image metadata to parse into python objects.
Using the requests library I can successfully get the result in XML format and then read the content with lxml. I am able to find the elements and explore the tree as expected:
# loading the response into an ElementTree
tree = etree.fromstring(response.content)
root = tree.getroot()
ns = root.nsmap
# get the first entry element and its summary
e = root.find('entry',ns)
summary = e.find('summary',ns).text
print summary
>> 'Date: 2018-11-28T09:10:56.879Z, Instrument: OLCI, Mode: , Satellite: Sentinel-3, Size: 713.99 MB'
The entry element has several date descendants with different values of the attriubute name:
for d in e.findall('date',ns):
print d.tag, d.attrib
>> {http://www.w3.org/2005/Atom}date {'name': 'creationdate'}
{http://www.w3.org/2005/Atom}date {'name': 'beginposition'}
{http://www.w3.org/2005/Atom}date {'name': 'endposition'}
{http://www.w3.org/2005/Atom}date {'name': 'ingestiondate'}
I want to grab the beginposition date element using XPath syntax [#attrib='value'] but it just returns None. Even just searching for a date element with the name attribute ([#attrib]) returns None:
dt_begin = e.find('date[#name="beginposition"]',ns) # dt_begin is None
dt_begin = e.find('date[#name]',ns) # dt_begin is None
The entry element includes other children that exhibit the same behaviour e.g. multiple str elements also with differing name attributes.
Has anyone encountered anything similar or is there something I'm missing? I'm using Python 2.7.14 with lxml 4.2.4
It looks like an explicit prefix is needed when a predicate ([#name="beginposition"]) is used. Here is a test program:
from lxml import etree
print etree.LXML_VERSION
tree = etree.parse("data.xml")
ns1 = tree.getroot().nsmap
print ns1
print tree.find('entry', ns1)
print tree.find('entry/date', ns1)
print tree.find('entry/date[#name="beginposition"]', ns1)
ns2 = {"atom": 'http://www.w3.org/2005/Atom'}
print tree.find('atom:entry', ns2)
print tree.find('atom:entry/atom:date', ns2)
print tree.find('atom:entry/atom:date[#name="beginposition"]', ns2)
Output:
(4, 2, 5, 0)
{None: 'http://www.w3.org/2005/Atom', 'opensearch': 'http://a9.com/-/spec/opensearch/1.1/'}
<Element {http://www.w3.org/2005/Atom}entry at 0x7f8987750b90>
<Element {http://www.w3.org/2005/Atom}date at 0x7f89877503f8>
None
<Element {http://www.w3.org/2005/Atom}entry at 0x7f8987750098>
<Element {http://www.w3.org/2005/Atom}date at 0x7f898774a950>
<Element {http://www.w3.org/2005/Atom}date at 0x7f898774a7a0>
I am trying to perform multiple sample comparison and Tukey HSD using the statsmodels module, but I keep getting this error message, "ValueError: v must be > 1 when p >= .9". I have tried looking this up on the internet for a possible solution, but no avail. Any chance anyone familiar with this module could help me out decipher what I am doing wrong to prompt this error. I use Python version 2.7x and spyder. Below is a sample of my data and the print statement. Thanks!
import numpy as np
from statsmodels.stats.multicomp import (pairwise_tukeyhsd,MultiComparison)
###--- Here are the data I am using:
data1 = np.array([ 1, 1, 1, 1, 976, 24, 1, 1, 15, 15780])
data2 = np.array(['lau15', 'gr17', 'fri26', 'bays29', 'dantzig4', 'KAT38','HARV50', 'HARV10', 'HARV20', 'HARV41'], dtype='|S8')
####--- Here's my print statement code:
print pairwise_tukeyhsd(data1, data2, alpha=0.05)
Seems you have to provide more data than a single observation per group, in order for the test to work.
Minimal example:
from statsmodels.stats.multicomp import pairwise_tukeyhsd,MultiComparison
data=[1,2,3]
groups=['a','b','c']
print("1st try:")
try:
print(pairwise_tukeyhsd(data,groups, alpha=0.05))
except ValueError as ve:
print("whoops!", ve)
data.append(2)
groups.append('a')
print("2nd try:")
try:
print( pairwise_tukeyhsd(data, groups, alpha=0.05))
except ValueError as ve:
print("whoops!", ve)
Output:
1st try:
/home/user/.local/lib/python3.7/site-packages/numpy/core/fromnumeric.py:3367: RuntimeWarning: Degrees of freedom <= 0 for slice
**kwargs)
/home/user/.local/lib/python3.7/site-packages/numpy/core/_methods.py:132: RuntimeWarning: invalid value encountered in double_scalars
ret = ret.dtype.type(ret / rcount)
whoops! v must be > 1 when p >= .9
2nd try:
Multiple Comparison of Means - Tukey HSD, FWER=0.05
====================================================
group1 group2 meandiff p-adj lower upper reject
----------------------------------------------------
a b 0.5 0.1 -16.045 17.045 False
a c 1.5 0.1 -15.045 18.045 False
b c 1.0 0.1 -18.1046 20.1046 False
----------------------------------------------------
I need to return the top 5 <Concelho> elements with the most <Habitante> grandchildren from Ano = 2001, but I'm having problems.
My code:
for $x in doc("Camaras.xml")/Portugal/Concelho
order by xs:integer($x/Habitantes/Habitante[#Ano = "2001"]) descending
return data($x[position() <= 5])
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE Portugal SYSTEM "CamarasDTD.dtd"> <Portugal>
<Concelho Nome="Arganil " id="0">
<Contactos>
<Email>geral#cm-arganil.pt</Email>
<Telefone> +351 235 200 150</Telefone>
<Fax> +351 235 200 158</Fax>
</Contactos>
<Localização>
<Codigo-Postal>3304-954 Arganil</Codigo-Postal>
</Localização>
<Mapa src="http://cim-regiaodecoimbra.pt/wp-content/uploads/2014/04/3D_arganil.png" />
<Habitantes>
<Habitante Ano="2001">2001</Habitante>
<Habitante Ano="2011">12145</Habitante>
</Habitantes>
</Concelho>
<Concelho Nome="Cantanhede " id="1">
<Contactos>
<Email>geral#cm-cantanhede.pt</Email>
<Telefone> +351 231 410 100</Telefone>
<Fax> +351 231 410 199</Fax>
</Contactos>
<Localização>
<Codigo-Postal>3060-133 Cantanhede</Codigo-Postal>
</Localização>
<Mapa src="http://cim-regiaodecoimbra.pt/wp-content/uploads/2014/04/3D_cantanhede1.png" />
<Habitantes>
<Habitante Ano="2001">37910</Habitante>
<Habitante Ano="2011">36595</Habitante>
</Habitantes>
</Concelho>
<Concelho Nome="Coimbra " id="2">
<Contactos>
<Email>geral#cm-coimbra.pt</Email>
<Telefone> +351 239 857 500</Telefone>
<Fax> +351 239 820 114</Fax>
</Contactos>
<Localização></Portugal>
The ordering is working correctly, but it's retuning all <Concelho> elements.
You need to do the sort, then filter:
let $foo :=
for $x in doc("Camaras.xml")/Portugal/Concelho
order by xs:integer($x/Habitantes/Habitante[#Ano = "2001"]) descending
return $x
return $foo[ position() <= 5 ]
I'm trying to filter xml file to get nodes with certain attribute. I can successfully filter by node (ex. \top_manager), but when I try \\top_manager[#salary='great'] I get nothing.
<?xml version= "1.0"?>
<employee xmlns="http://www.w3schools.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="employee.xsd">
<top_manager>
<ceo salary="great" respect="enormous" type="extra">
<fname>
Vasya
</fname>
<lname>
Pypkin
</lname>
<hire_date>
19
</hire_date>
<descr>
Big boss
</descr>
</ceo>
<cio salary="big" respect="great" type="intro">
<fname>
Petr
</fname>
<lname>
Pypkin
</lname>
<hire_date>
25
</hire_date>
<descr>
Resposible for information security
</descr>
</cio>
</top_manager>
......
How I need to correct this code to get what I need?
require 'nokogiri'
f = File.open("employee.xml")
doc = Nokogiri::XML(f)
doc.xpath("//top_manager[#salary='great']").each do |node|
puts node.text
end
thank you.
That's because salary is not attribute of <top_manager> element, it is the attribute of <top_manager>'s children elements :
//xmlns:top_manager[*[#salary='great']]
Above XPath select <top_manager> element having any of it's child element has attribute salary equals "great". Or if you meant to select the children (the <ceo> element in this case) :
//xmlns:top_manager/*[#salary='great']
I want to get a set of elements from a xml-file, but as soon the the elements involve namespaces, it fails.
This is a fragment of the xml file:
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
version="1.0" creator="Groundspeak Pocket Query"
xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.groundspeak.com/cache/1/0 http://www.groundspeak.com/cache/1/0/cache.xsd"
xmlns="http://www.topografix.com/GPX/1/0">
<name>My Finds Pocket Query</name>
<desc>Geocache file generated by Groundspeak</desc>
<author>Groundspeak</author>
<email>contact#groundspeak.com</email>
<time>2010-09-15T16:18:55.9846906Z</time>
<keywords>cache, geocache, groundspeak</keywords>
<bounds minlat="41.89687" minlon="5.561883" maxlat="70.669967" maxlon="25.74735" />
<wpt lat="62.244933" lon="25.74735">
<time>2010-01-11T08:00:00Z</time>
<name>GC22W1T</name>
<desc>Kadonneet ja karanneet by ooti, Traditional Cache (1.5/2)</desc>
<url>http://www.geocaching.com/seek/cache_details.aspx?guid=4af28fe9-401b-44df-b058-5fd5399fc083</url>
<urlname>Kadonneet ja karanneet</urlname>
<sym>Geocache Found</sym>
<type>Geocache|Traditional Cache</type>
<groundspeak:cache id="1521507" available="True" archived="False" xmlns:groundspeak="http://www.groundspeak.com/cache/1/0">
<groundspeak:name>Kadonneet ja karanneet</groundspeak:name>
<groundspeak:placed_by>ooti</groundspeak:placed_by>
<groundspeak:owner id="816431">ooti</groundspeak:owner>
<groundspeak:type>Traditional Cache</groundspeak:type>
<groundspeak:container>Small</groundspeak:container>
<groundspeak:difficulty>1.5</groundspeak:difficulty>
<groundspeak:terrain>2</groundspeak:terrain>
<groundspeak:country>Finland</groundspeak:country>
<groundspeak:state>
</groundspeak:state>
<groundspeak:short_description html="True">
</groundspeak:short_description>
<groundspeak:encoded_hints>
</groundspeak:encoded_hints>
<groundspeak:travelbugs />
</groundspeak:cache>
</wpt>
</gpx>
I want to get all the grounspeak:cache elements, but neither //groundspeak:cache nor //cache seems to return anything.
NSArray *caches = [self.xml nodesForXPath:#"//cache" error:&error];
Any clue?
Edit: Are there any cocoa-based software out there, where I can load my xml and test different xpaths? I'm quite new to objective-c and cocoa, so it would be nice to check that it is really my xpath that is wrong..
This //cache means: a descendant element under no namespace (or empty namespace)
Your groundspeak:cache element is under a namespace URI http://www.groundspeak.com/cache/1/0.
So, if you can't declare a namespace-prefix binding (I think you can't with cocoa...), you could use this XPath expression:
//*[namespace-uri()='http://www.groundspeak.com/cache/1/0' and
local-name()='cache']
If you don't want to be so strict about namespace...
//*[local-name()='cache']
But this last is a bad practice, because you could end up selecting wrong nodes, and because when dealing with XML, your tool should support namespaces.
As proof, this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:copy-of select="//*[namespace-uri() =
'http://www.groundspeak.com/cache/1/0' and
local-name() = 'cache']"/>
</xsl:template>
</xsl:stylesheet>
Output:
<groundspeak:cache id="1521507" available="True" archived="False"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.topografix.com/GPX/1/0"
xmlns:groundspeak="http://www.groundspeak.com/cache/1/0">
<groundspeak:name>Kadonneet ja karanneet</groundspeak:name>
<groundspeak:placed_by>ooti</groundspeak:placed_by>
<groundspeak:owner id="816431">ooti</groundspeak:owner>
<groundspeak:type>Traditional Cache</groundspeak:type>
<groundspeak:container>Small</groundspeak:container>
<groundspeak:difficulty>1.5</groundspeak:difficulty>
<groundspeak:terrain>2</groundspeak:terrain>
<groundspeak:country>Finland</groundspeak:country>
<groundspeak:state></groundspeak:state>
<groundspeak:short_description html="True"></groundspeak:short_description>
<groundspeak:encoded_hints></groundspeak:encoded_hints>
<groundspeak:travelbugs />
</groundspeak:cache>
You need to add a new namespace attribute to the root node of your document, defining a prefix that you can use when querying the children:
NSXMLDocument *xmldoc = ...
NSXMLElement *namespace = [NSXMLElement namespaceWithName:#"mns" stringValue:#"http://mynamespaceurl.com/mynamespace"];
[xmldoc.rootElement addNamespace:namespace];
then when you query things later, you can use that prefix to refer to the namespace:
NSArray * caches = [xmldoc.rootElement nodesForXPath:#"//mns:caches" error:&error];
//groundspeak:cache should work. You might need a namespace-uri setting as well