Xpath - how to differentiate nodes without attributes? - xpath

I want to extract a value from xml via xpath and I'm struggling a bit. This is the example of xml I have to work with
<data>
<menu>
<date>2017-10-30</date>
<type>S</type>
<name>onion soup</name>
</menu>
<menu>
<date>2017-10-30</date>
<type>L</type>
<name>ham sandwich</name>
</menu>
<menu>
<date>2017-10-31</date>
<type>S</type>
<name>pumpkin soup</name>
</menu>
<menu>
<date>2017-10-31</date>
<type>L</type>
<name>cheese sandwich</name>
</menu>
<menu>
<date>2017-11-1</date>
<type>S</type>
<name>sweet potato soup</name>
</menu>
<menu>
<date>2017-11-1</date>
<type>L</type>
<name>chicken sandwich</name>
</menu>
</data>
The dates and meal names are dynamically changing.
Now I have 2 columns, for Today's soup and Tomorrow's. I know how to link to xml via xpath for today's soup:
/data/menu/name[../type/text() = "S"] or /data/menu[type[text()='S']]/name
But I struggle with tomorrow's as my xml feed doesn't have any attributes to differentiate, types are the same for both dates and date is constantly changing.
Thanks for any help.
Edit:
Thank you for anwering.
I think I described my problem wrong.
I should probably point out that I'm using Xpath build-in feature in one of the local software.
You're right, these lines
/data/menu[type='S' and date='2017-10-31']]/name
are for all the soups, I just wrongly described it by how it behaves on my end, where it gives me just the value of the first one.
/data/menu[type='S' and date='2017-11-01']]/name
will give me Tommorow's soup, but if I want to use output for this value in static column "Tommorow's soup" next to which I want my xpath output it will only be true for one day. What I need is for it to be true also for next days.
I need a line that will give me "tommorow's soup" which is suppose to be Pumpkin soup today, tommorow when the xml updates it would be Sweet Potato soup and day after that it will be some new soup which is going to be updated later with the whole xml.
If I use
/data/menu[type='S' and date='2017-10-30']]/name
it will not show anything tommorow since there won't be a 2017-10-30 because the xml will update and will start with 2017-10-31.
I hope it's clearer now what I'm asking. I know it's still confusing it's kinda hard for me to describe it in English especially since I'm beginner when it comes to Xpath.

How to differentiate elements without attributes? Use other elements...
But first to clear up a wrong assumption:
Now I have 2 columns, for Today's soup and Tomorrow's. I know how to
link to xml via xpath for today's soup:
/data/menu/name[../type/text() = "S"] or
/data/menu[type[text()='S']]/name
Actually, the XPaths that you say will give you today's soups will actually give you all soups regardless of date.
XPath 1.0
XPath 1.0 has no date functions1, so you'll have to pass the current date and tomorrow's date into your XPAth, and you're on your own to test the date element's value as a string:
If today is 2017-10-31, then this XPath will give you the names of today's soups,
/data/menu[type='S' and date='2017-10-31']]/name
and this XPath will give you the names of tomorrow's soups:
/data/menu[type='S' and date='2017-11-1']]/name
1
XPath 2.0 and 3.0's dynamic context includes a current-dateTime() function, but its format is implementation-dependent, which limits its usefulness. You might be able to use date calculations to determine tomorrow's date, but unless you want to be dependent upon an implementation-defined format for current-dateTime(), you'll have to pass today into your XPath at least.

Related

How to specific a certain row in an XML based Jasper Report

I'm trying to use an XML data source iReport Template, however, I need to call out certain rows in Jasper from a listed object.
<week-dates>
<date dayNumber="1" iso="2021-07-12" formatted="07/11/2021" dayOfWeek="SUNDAY" dayOfWeekShort="Sun"/>
<date dayNumber="2" iso="2021-07-13" formatted="07/12/2021" dayOfWeek="MONDAY" dayOfWeekShort="Mon"/>
<date dayNumber="3" iso="2021-07-14" formatted="07/13/2021" dayOfWeek="TUESDAY" dayOfWeekShort="Tue"/>
<date dayNumber="4" iso="2021-07-15" formatted="07/14/2021" dayOfWeek="WEDNESDAY" dayOfWeekShort="Wed"/>
<date dayNumber="5" iso="2021-07-16" formatted="07/15/2021" dayOfWeek="THURSDAY" dayOfWeekShort="Thu"/>
<date dayNumber="6" iso="2021-07-17" formatted="07/16/2021" dayOfWeek="FRIDAY" dayOfWeekShort="Fri"/>
<date dayNumber="7" iso="2021-07-18" formatted="07/17/2021" dayOfWeek="SATURDAY" dayOfWeekShort="Sat"/>
</week-dates>
I can get the first object, using week-dates/date/#iso inside the page header band, But this should be used to iterate through the object in the detail band. However, I need fields to specifically call out certain rows, for example week-dates/date/iso[2] to call out iso date from dayNumber 3. However, I can't seem to get that to work.
Does anyone have any suggestions on how I can get a specific line in this list so I can use text fields in horizontal order?
While week-dates/date[5]/#iso will work for this specific example, it may be a better idea to specify "the date element that has a dayNumber attribute equal to 5" in the XPath.
week-dates/date[#dayNumber=5]/#iso
I figured it out, the element index needed to be on the date element, not the iso level
week-dates/date[5]/#iso
Gave the expected result

XPATH How to Select two specific chidren from a same parent

Good night, friends!
Lets suppose we have a xml with 30 items like that and I want to get just the name and url from an item where name contains the word: Richard.
<channel>
<item>
<name>Brian</name>
<lastname>Connor</lastname>
<age>40</age>
<enclosure url="http://www.brian.com"/>
</item>
<item>
<name>Richard</name>
<lastname>Wendell</lastname>
<age>38</age>
<enclosure url="http://www.richard.com"/>
</item>
</channel>
How can I do that using XPath?
I tried:
"//channel/item[name[contains(text(),'Richard')]]" but it returns just the name and I don't know how to select the url information together.
Please excuse my english!
Your approach does not work because you are selecting a sub tree (in this case an item) of the XML tree which contains more information than you want. If you want just a subset of the attributes in ONE xpath expression you have to select them separately and then concatenate them adequately, e.g.
concat('name=', //channel/item[contains(name, 'Richard')]/name, ' url=', //channel/item[contains(name, 'Richard')]/enclosure/#url)
The example will allow you to alter the additional formatting easily.
By the way: your XML input was malformatted. I corrected this.

Navigating XML via Xpath

I am new to Xpath and have been trying to get my head around some basic examples using xpath testing sites before I tackle a much more complex piece.
I am trying to understand exactly how to use the contains function in conjunction with other condition(s), but struggling a bit.
Here is my xml:
<root xmlns:foo="http://www.foo.org/" xmlns:bar="http://www.bar.org">
<actors>
<actor id="1">Christian Bale</actor>
<actor id="2">Liam Neeson</actor>
<actor id="3">Michael Caine</actor>
</actors>
<foo:singers>
<foo:singer id="4">Tom Waits</foo:singer>
<foo:singer id="4">B.B. King</foo:singer>
<foo:singer id="6">Ray Charles</foo:singer>
</foo:singers>
</root>
To replicate the type of xml (or html to be more precise) I am trying to parse, I have got one of the singer attributes repeated.
So I am trying to use contains to find the foo:singer id = 4 and contains "Tom Waits".
From what I have read and examples I have seen, you can use this type of expression:
.//*[#id =4 and //foo:singer[contains(text(),'Tom Waits')]]/text()
However, this returns both Tom Waits and BB King.
If I use these two expressions separately, they get the expected results, so not sure exactly if/how they can be combined.
Many thanks if you can assist me.
Andrew
Be sure to pay attention to context. There's no need for the nested predicate.
.//*[#id =4 and contains(.,'Tom Waits')]/text()
So I am trying to use contains to find the foo:singer id = 4 and contains "Tom Waits".
Since you're using //foo:singer for the contains test, the entire document is in context so it's always true.
Use
//foo:singer[contains(text(),'Tom Waits')]/text()

It is possible to use Xpath function in order to read the date value

Since i am new to Xpath i want to ask if it is possible to use Xpath function in order to read the date value in the following
<div id="qa-case">
<time itemprop="datePublished" datetime="2015-01-12T02:41:00Z"></time>
</div>
What I want is the value in the datetime. Is that possible to read it using something like this
//*[#id="qa-case"]fn:string(datetime)
What i expect is to have an text showing me 2015-01-12T02:41:00Z
Thanks in advance
We do not know what version of XPath you are using. In XPath 1.0, functions must be wrapped around everything else because they cannot be steps in a path expression:
string(//div[#id = 'qa-case']/time/#datetime)
This only works with exactly one time element node and thus with one datetime attribute.
In XPath 2.0 you could also do
//div[#id = 'qa-case']/time/#datetime/string()
The result, in both cases, is
2015-01-12T02:41:00Z
To give a more specific answer, we would need to know more about the environment you use XPath in (say, XSLT).
To get just the text, you don't really need any date functions, just //div[#id="qa-case"]/#datetime
If you want to convert to a more readable format you cna then use date-specific XPath functions, for example:
<p>Today is <xsl:value-of="fn:day-from-dateTime(#datetime)" </p>

XPath concat multiple nodes

I'm not very familiar with xpath. But I was working with xpath expressions and setting them in a database. Actually it's just the BAM tool for biztalk.
Anyway, I have an xml which could look like:
<File>
<Element1>element1<Element1>
<Element2>element2<Element2>
<Element3>
<SubElement>sub1</SubElement>
<SubElement>sub2</SubElement>
<SubElement>sub3</SubElement>
<Element3>
</File>
I was wondering if there is a way to use an xpath expression of getting all the SubElements concatted? At the moment, I am using:
/*[local-name()='File']/*[local-name()='Element3']/*[local-name()='SubElement']
This works if it only has one index. But apparently my xml sometimes has more nodes, so it gives NULL. I could just use
/*[local-name()='File']/*[local-name()='Element3']/*[local-name()='SubElement'][0]
but I need all the nodes. Is there a way to do this?
Thanks a lot!
Edit: I changed the XML, I was wrong, it's different, it should look like this:
<item>
<element1>el1</element1>
<element2>el2</element2>
<element3>el3</element3>
<element4>
<subEl1>subel1a</subEl1>
<subEl2>subel2a</subEl2>
</element4>
<element4>
<subEl1>subel1b</subEl1>
<subEl2>subel2b</subEl2>
</element4>
</item>
And I need to have a one line code to get a result like: "subel2a subel2b";
I need the one line because I set this xpath expression as an xml attribute (not my choice, it's specified). I tried string-join but it's not really working.
string-join(/file/Element3/SubElement, ',')
/File/Element3/SubElement will match all of the SubElement elements in your sample XML. What are you using to evaluate it?
If your evaluation method is subject to the "first node rule", then it will only match the first one. If you are using a method that returns a nodeset, then it will return all of them.
You can get all SubElements by using:
//SubElement
But this won't keep them grouped together how you want. You will want to do a query for all elements that contain a SubElement (basically do a search for the parent of any SubElements).
//parent::SubElement
Once you have that, you could (depending on your programming language) loop through the parents and concatenate the SubElements.

Resources