Xpath Axes - how to select child node attribute - xpath

I have the following XML:
<ArrayOfStationStatus xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autopagerMatchedRules="1">
<StationStatus ID="20" StatusDetails="To the platform due to planned maintenance work.">
<Station ID="20" Name="Bermondsey"/>
<Status ID="NS" CssClass="Closed" Description="No Step Free Access" IsActive="true">
<StatusType ID="2" Description="Station"/>
</Status>
</StationStatus>
</ArrayOfStationStatus>
And would like to select StationStatus nodes that contain a particular phrase in the Name attribute. It's important that I select SationStatus nodes.
This is the xpath I have come up with but it's not correct:
/ArrayOfStationStatus/StationStatus[contains(lower-case(child::Station/#Name),lower-case('phrase'))]
EDIT::::::::
I just solved it! This is the code I needed:
/ArrayOfStationStatus/StationStatus[child::Station[contains(lower-case(attribute::Name),lower-case("Ac"))]]

Well I managed to solve it people! Here is the solution, in this case I'm looking for the phrase 'Ac' as you can see
/ArrayOfStationStatus/StationStatus[child::Station[contains(lower-case(attribute::Name),lower-case("Ac"))]]
Also remember
lower-case(
is only available in xpath 2.0 (Dimitre Novatchev)

Related

Xquery recursion

I have a question in regards to Xpath, which is something that I am very experienced at. I am doing an assignment where I am supposed to invert all the elements so that the sub elements inside of the Music tag becomes attributes and so that the attributes become sub elements.
Is it possible to do this using Xpath, and how should I got about it? Or is it better to write a DTD of the XML file and write a solution in Xquery?
My XML looks like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<music>
<song genre="rocknroll" album="1134" artist="249" id="11992"><name>The Sound of Silence</name><nr>1</nr></song>
<song genre="rocknroll" album="4329" artist="932" id="34311"><name>Material Girl</name><nr>2</nr></song>
<song genre="hardrock" album="5293" artist="1167" id="54312"><name>Paradise City</name><nr>1</nr></song>
<song genre="classic" album="7002" artist="156" id="78340"><name>La Primavera</name><nr>1</nr></song>
<artist id="249" isband="yes">Simon and Garfunkel</artist>
<artist id="932" isband="no">Madonna</artist>
<artist id="1167" isband="yes">Guns N'Roses</artist>
<artist id="156" isband="no">Antonio Vivaldi</artist>
<album issued="1964" id="1134" label="Columbia Records">Wednesday Morning, 3 AM</album>
<album issued="1984" id="4392" label="Sire Records">Like A Virgin</album>
<album issued="1988" id="5293" label="Geffen Records">Appetite for Destruction</album>
<album issued="2004" id="7002" label="Naxos Records" performers="Wiener Philharmoniker">Le Quattro Stagioni</album>
</music>
Thanks in advance for any help.
XPath can only select nodes in the input tree, or compute new atomic values (like strings and numbers). It cannot construct a new tree. For that you need XSLT or XQuery.
Writing a DTD might be useful for other reasons, but it won't make it any easier to produce the required XSLT or XQuery code for this problem.

Xpath - How to select subnode where sibling-node contains certain text

I want to use XPath to select the sub tree containing the <name>-tag with "ABC" and not the other one from the following xml. Is this possible? And as a minor question, which keywords would I use to find something like that over Google (e.g. for selecting the sub tree by an attribute I would have the terminology for)?
<root>
<operation>
<name>ABC</name>
<description>Description 1</description>
</operation>
<operation>
<name>DEF</name>
<description>Description 2</description>
</operation>
</root>
Use:
/*/operation[name='ABC']
For your second question: I strongly recommend not to rely on online sources (there are some that aren't so good) but to read a good book on XPath.
See some resources listed here:
https://stackoverflow.com/questions/339930/any-good-xslt-tutorial-book-blog-site-online/341589#341589
For your first question, I think a more accurate way to do it would be://operation[./name[text()='ABC']].And according to this , we can also make it://operation[./name[text()[.='ABC']]]

XPath-REXML-Ruby: Selecting multiple siblings/ancestors/descendants

This is my first post here. I have just started working with Ruby and am using REXML for some XML handling. I present a small sample of my xml file here:
<record>
<header>
<identifier>oai:lcoa1.loc.gov:loc.gmd/g3195.ct000379</identifier>
<datestamp>2004-08-13T15:32:50Z</datestamp>
<setSpec>gmd</setSpec>
</header>
<metadata>
<titleInfo>
<title>Meet-konstige vertoning van de grote en merk-waardige zons-verduistering</title>
</titleInfo>
</metadata>
</record>
My objective is to match the last numerical value in the tag with a list of values that I have from an array. I have achieved this with the following code snippet:
ids = XPath.match(xmldoc, "//identifier[text()='oai:lcoa1.loc.gov:loc.gmd/"+mapid+"']")
Having got a particular identifier that I wish to investigate, now I want to go back to and select and then select to get the value in the node for that particular identifier.
I have looked at the XPath tutorials and expressions and many of the related questions on this website as well and learnt about axes and the different concepts such as ancestor/following sibling etc. However, I am really confused and cannot figure this out easily.
I was wondering if I could get any help or if someone could point me towards an online resource "easy" to read.
Thank you.
UPDATE:
I have been trying various combinations of code such as:
idss = XPath.match(xmldoc, "//identifier[text()='oai:lcoa1.loc.gov:loc.gmd/"+mapid+"']/parent::header/following-sibling::metadata/child::mods/child::titleInfo/child::title")
The code compiles but does not output anything. I am wondering what I am doing so wrong.
Here's a way to accomplish it using XPath, then going up to the record, then XPath to get the title:
require 'rexml/document'
include REXML
xml=<<END
<record>
<header>
<identifier>oai:lcoa1.loc.gov:loc.gmd/g3195.ct000379</identifier>
<datestamp>2004-08-13T15:32:50Z</datestamp>
<setSpec>gmd</setSpec>
</header>
<metadata>
<titleInfo>
<title>Meet-konstige</title>
</titleInfo>
</metadata>
</record>
END
doc=Document.new(xml)
mapid = "ct000379"
text = "oai:lcoa1.loc.gov:loc.gmd/g3195.#{mapid}"
identifier_nodes = XPath.match(doc, "//identifier[text()='#{text}']")
record_node = identifier_nodes.first.parent.parent
record_node.elements['metadata/titleInfo/title'].text
=> "Meet-konstig"

How to find all nodes of a specific type in XPath

Lets say i have the following form data instance in my view.xml:
<xhtml:html xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xxforms="http://orbeon.org/oxf/xml/xforms"
xmlns:exforms="http://www.exforms.org/exf/1-0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xhtml:head>
<xforms:instance id="instanceData">
<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<fruits>
<fruit>
<fruit-name>Mango</fruit-name>
</fruit>
<fruit>
<fruit-name>Apple</fruit-name>
</fruit>
<fruit>
<fruit-name>Banana</fruit-name>
</fruit>
</fruits>
</form>
</xforms:instance>
</xhtml:head>
I want to select all the fruit names from the above instance.
I tried the following ways but it always selects the first fruit.
instance('instanceData')/fruits/fruit[*]/fruit-name
instance('instanceData')/fruits/fruit/fruit-name
instance('instanceData')/fruits/fruit[position()>0]/fruit-name
Please provide a way to overcome this in XPATH
try this
"//fruit-name"
It shall find all fruitnames wherever they are in the document hierarchy.
If you want to select all the <fruit-name> from the instance instanceData (<xforms:instance id="instanceData">) that looks like the one you have in your question, the following should do it:
instance('instanceData')/fruits/fruit/fruit-name
If this doesn't work, one common reason is that you have a default namespace declaration in the document that contains your instance, like: xmlns="http://www.w3.org/1999/xhtml". If you have this, you need to undo that default namespace declaration on where you declare the instance, with:
<xforms:instance xmlns="" id="instanceData">
(And if this is the issue, my advice is not to use default namespace declarations. Ever. Instead declare xmlns:xhtml="http://www.w3.org/1999/xhtml" and use the xhtml prefix everywhere.)
First:
It may be a typo any way to point out you xml has wrong node ending
<service>
Second:
your XPATH is very much valid but when you parse it out you need to iterate over the result set as like its a sequence of node and not a single value.
e.g) in JDOM :< Element.selectObject Vs selectSingleNodes Vs selectAsArray kind.
In your XForms you need to iterate over the resultset to get the list of fruits.
if you want only fruit names then you could try
instance('instanceData')/fruits/fruit/fruit-name/text()

Select a only first matching node in XPath

I have the following XML:
<parent>
<pet>
<data>
<birthday/>
</data>
</pet>
<pet>
<data>
<birthday/>
</data>
</pet>
</parent>
And now I want to select the first birthday element via parent//birthday[1] but this returns both birthday elements because bothof them are the first child of their parents.
How can I only select the first birthday element of the entire document no matter where it is located. I've tried parent//birthday[position()=1] but that doesn't work either.
You mean (note the parentheses!)
(/parent/pet/data/birthday)[1]
or, a shorter, but less specific variation:
(/*/*/*/birthday)[1]
(//birthday)[1]
or, more semantic, the "birthday of the first pet":
/parent/pet[1]/data/birthday
or, if not all pets have birthday entries, the "birthday of the first pet that for which a birthday is set":
/parent/pet[data/birthday][1]/data/birthday
If you work from a context node, you can abbreviate the expression by making it relative to that context node.
Explanation:
/parent/pet/data/birthday[1] selects all <birthday> nodes that are the first in their respective parents (the <data> nodes), throughout the document
(/parent/pet/data/birthday)[1] selects all <birthday> nodes, and of those (that's what the parentheses do, they create an intermediary node-set), it takes the first one
FYI: you can visualize the results of the various Xpath queries with the (free) XPathVisualizer tool. Works on Windows only.
Ok, I admit this is horrendous and there must be a better way, but it appears to work.
/*/*[descendant::birthday and not(preceding-sibling::*[descendant::birthday])]
I look for all elements at the second level in the tree that have a descendant element called birthday that do not have a preceding sibling element that has a birthday element as a descendant.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="birthdays" select="//birthday"/>
<xsl:value-of select="$birthdays[1]"/>
</xsl:template>
</xsl:stylesheet>
try
//birthday[position()=1]
// finds nodes no matter where there are in the hierarchy
you could also do
pet[position()=1]/data/birthday

Resources