<parent>
...
<child>foo</child>
<child current="true">current</child>
<child>bar</child>
...
</parent>
How would I get the values of the children before and after the #current child (i.e. output foo and bar)? I'll be outputting Prev/Next links using those values. Thanks!
child[#current='true']/preceding-sibling::child[1]
child[#current='true']/following-sibling::child[1]
Related
XPath newbie here.
Is there a way to select a list of attr1 and child3 pairs using XPath from the sample XML below?
In other words I need a list of a11, c13, a21, c23, etc.
Or I can only do //parent and pick required values from the array of resulting nodes?
<parentList>
<parent attr1="a11" attr2="a12">
<child1>c11</child1>
<child2>c12</child2>
<child3>c13</child3>
<child4>c14</child4>
<child5>c15</child5>
</parent>
<parent attr1="a21" attr2="22">
<child1>c21</child1>
<child2>c22</child2>
<child3>c23</child3>
<child4>c24</child4>
<child5>c25</child5>
</parent>
</parentList>
This xpath 2.0 expression (and properly closing your sample html)
//parent/concat(#attr1,' ',child3/text())
should output:
a11 c13
a21 c23
XPath 1.0 solution to select the elements of interest :
//parent/descendant-or-self::*[position()=1 or position()=4]
But, to generate a list of data you should use the following one :
(//#attr1|//text()[normalize-space()])[parent::parent or parent::*[parent::parent][count(preceding-sibling::*)=2]]
Look for an attribute or text with : parent element as parent or child of parent as parent and two preceding siblings.
Output : a11,c13,a21,c23
I am trying to reference a node in an expression. Take this simple example:
<?xml version="1.0" encoding="UTF-8" ?>
<homelist>
<homes>
<home>
<hname>house</hname>
<location>hell</location>
<url>wee</url>
<cID>1234</cID>
</home>
</homes>
<contacts>
<contactdetails cID="1234">
<cname>John Smith</cname>
<phone>0123234</phone>
<email>test#gmail.com</email>
</contactdetails>
</contacts>
</homelist>
I basically want to select nodes if it's value is somewhere else in the tree.
For example, I want to display the url of homes that have cID of John Smith. I tried this but it doesn't work, what is wrong with it:
homelist/homes/home[ancestor::homelist/contacts/contactdetails[cname="John Smith"]/url
"/homelist/homes/home[cID = /homelist/contacts/contactdetails[cname='John Smith']/#cID]/url"
You want to find the <home> whose <cID> child's text content equals that of the cID= attribute of the <contactdetails> whose <cname> contains 'John Smith', then return its <url> child.
Note that I've written this as an absolute path, from the root, since you didn't tell us what the context node was going to be for this XPath.
There are certainly other ways of writing the same concept; this is just the first one that occurred to me offhand.
If you preferred to use ancestor or parent, you could say
"/homelist/homes/home[cID = ancestor::homelist/contacts/contactdetails[cname='John Smith']/#cID]/url"
I am using Ruby to retrieve an XML document with the following format:
<project>
<users>
<person>
<name>LUIS</name>
</person>
<person>
<name>JOHN</name>
</person>
</users>
</project>
I want to know how to produce the following result, with the tags concatenated:
<project>
<users>
<person>
<name>LUIS JOHN</name>
</person>
</users>
</project>
Here is the code I am using:
file = File.new( "proyectos.xml" )
doc3 = Nokogiri::XML(file)
a=0
#participa = doc3.search("person")
#participa.each do |i|
#par = #participa.search("name").map { |node| node.children.text }
#par.each do |i|
puts #par[a]
puts '--'
a = a + 1
end
end
Rather than supply code, here's how to fish:
To parse your XML into Nokogiri, which I recommend highly:
require 'nokogiri'
doc = Nokogiri::XML(<<EOT)
<project>
<users>
<person>
<name>LUIS</name>
</person>
<person>
<name>JOHN</name>
</person>
</users>
</project>
EOT
That gives you a doc variable which is the DOM as a Nokogiri::XML::Document. From that you can search, either for matching nodes or a particular node. search allows you to pass an XPath or CSS accessor to locate what you are looking for. I recommend CSS for most things because it is more readable, but XPath has some great tools to dig into the structure of your XML, so often I end up with both in my code.
So, doc.at('users') is the CSS accessor to find the first users node. doc.search('person') will return all nodes matching the person tag as a NodeSet, which is basically an array which you can enumerate or loop over.
Nokogiri has a text method for a node that lets you get the text content of that node, including all the carriage-returns between nodes that would normally be considered formatting in the XML as it flows down the document. When you have the text of the node, you can apply the normal Ruby string processing commands, such as strip, squish, chomp, etc., to massage the text into a more usable format.
Nokogiri also has a children= method which lets you redefine the child nodes of a node. You can pass in a node you've created, a NodeSet, or even the text you want rendered into the XML at that point.
In a quick experiment, I have code that does what you want in basically four lines. But, I want to see your work before I share what I wrote.
Finally, puts doc.to_xml will let you easily see if your changes to the document were successful.
Here's how I'd do it:
require 'nokogiri'
doc = Nokogiri::XML(<<EOT)
<project>
<users>
<person>
<name>LUIS</name>
</person>
<person>
<name>JOHN</name>
</person>
</users>
</project>
EOT
The XML is parsed into a DOM now. Search for the users tags, then locate the embedded name tags and extract the text from them. Join the results into a single space-delimited string. Then replace the children of the users tag with the desired results:
doc.search('users').each do |users|
user_names = users.search('name').map(&:text).join(' ')
users.children = "<person><name>#{ user_names }</name></person>"
end
If you output the resulting XML you'll get:
puts doc.to_xml
<?xml version="1.0"?>
<project>
<users><person><name>LUIS JOHN</name></person></users>
</project>
I have a hugely nested xml tree that I want to be able to query with xpath and only pick out selected nodes from each section of the tree. For example in the following from each parent node i would like to get childTree > a and c > cb:
<parents>
<parent>
<childTree>
<a></a>
<b></b>
<c>
<ca></ca>
<cb></cb>
</c>
</childTree>
</parent>
<parent>
<childTree>
<a></a>
<b></b>
<c>
<ca></ca>
<cb></cb>
</c>
</childTree>
</parent>
<parent>
<childTree>
<a></a>
<b></b>
<c>
<ca></ca>
<cb></cb>
</c>
</childTree>
</parent>
<parents>
Would the following work for you? In Xpath you can use '|' as a union and do something like this:
//childTree/a|//childTree/c/cb
Given this xml document:
<?xml version="1.0" encoding="UTF-8"?>
<mydoc>
<foo f="fooattr">
<bar r="barattr1">
<baz z="bazattr1">this is the first baz</baz>
</bar>
<bar r="barattr2">
<baz z="bazattr2">this is the second baz</baz>
</bar>
</foo>
</mydoc>
that is being processed by this xquery:
let $d := doc('file:///Users/mark/foo.xml')
let $barnode := $d/mydoc/foo/bar/baz[contains(#z, '2')]
let $foonode := $barnode/../../#f
return $foonode
I get the following error:
"Cannot create an attribute node (f) whose parent is a document node".
It seems that the ../ operation is sort of removing the matching nodes from the rest of the document such that it thinks it's the document node.
I'm open to other approaches but the selection of the parent depends on the child attribute containing a certain sub-string.
Cheers!
The query you have written is selecting the attribute f. However it is not legal to return an attribute node from an XQuery. The error is refering to the output document which here contains just an attribute (although this error message is misleading, as technically there is no output document here, there is just an attribute node that is returned).
You probably wanted to return the value of the attribute rather than the attribute itself
return data($foonode)