XPATH Expression help needed - xpath

I have an XML file:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<nodeA>
</nodeA>
<nodeB>
<nodeB></nodeB>
<nodeB></nodeB>
<nodeB></nodeB>
<nodeB></nodeB>
</nodeB>
<nodeC>
</nodeC>
</root>
I want to write an XPATH expression which will select the <nodeB> which is the parent of the other <nodeB> nodes. I tried something like
"//nodeB/nodeB/parent::nodeB"
but it also choses <nodeC> besides the one I want.
Can you please help?
thanks
mc

Try this (nodeB having a child of nodeB)
//nodeB[nodeB]

Related

Get the position of an element with specific attribute value

I'm trying to get with xPath the position only of the first element which has the attribute value true.
<?xml version="1.0" encoding="UTF-8"?>
<elements>
<element attribute="false"/>
<element attribute="true"/>
<element attribute="true"/>
</elements>
What I have so fare is:
head(/elements/element[#attribute='true']/position())
Result:
1
But it should be:
2
What am I doing wrong?
position() returns the position of the element in the nodelist created by the predicate, i.e. with the false excluded. Instead of position, you can e.g. count the number of preceding elemements.
For example, this works even in XPath 1.0:
1+count(/elements/element[#attribute="true"][1]/preceding-sibling::element)
I think it's (with XPath 3):
head(index-of(/elements/element/#attribute, 'true'))
saxon-lint --xpath 'count(//element[#attribute="true"]/position())' file.xml
From Michael answer:
saxon-lint --xpath 'head(index-of(/elements/element/#attribute, "true"))' file.xml
Output
2

Compare multiple strings in XPath

Is there any better way to write things like [node/text()="a" or node/text()="b"] like this [contains(arraytype("a", "b"), node/text())]? Does the array type exist in XPath and can I use it inside the contains function to write more readable code?
Thanks in advance. :)
Code :
require xpath > 1
saxon-lint --xpath '/students/student/name[text()=("A", "B")]' file.xml
Output :
<name>A</name>
Files :
<?xml version="1.0"?>
<students>
<student>
<stuId>1</stuId>
<name>A</name>
<mark>75</mark>
<result></result>
</student>
</students>
Check
saxon-lint (my own project)
Thanks #Andersson for the tip

reading value actual from xml path

I have the following xml structure:
<?xml version="1.0" encoding="UTF-8"? >
<sql>
<Assoc name="sql">
<RecArray name="contents">
<Record name="contents">
<String name="PackType" > < value actual="P" />< /String >
<String name="SerialNumber" > < value actual="0002" />< /String >
<String name="VersionNumber" > < value actual="02" /></ String >
</Record>
</RecArray>
</Assoc>
</sql>
how can i get the values of each of the String nodes like i need to know the value inside the node of "SerialNumber"
Regards,
If you wan to get all <value> elements inside each <String> element, you can try this XPath query :
/sql/Assoc/RecArray/Record/String/value
precise path will be better performance wise. If you're looking for simpler query, this will also work :
//String/value
or if you mean by values of each of the String nodes is value of actual attribute, you can do this way :
/sql/Assoc/RecArray/Record/String/value/#actual
Finally, if none of above meet your requirement, please update the question and provide expected output from sample XML posted.
i figured it out
as it is multi String elements (that was clear in the question), i should use the following
/sql/Assoc/RecArray/Record/String[2]/value/#actual

How can I concatenate two XML tags using Ruby/Nokogiri?

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>

How can I use Ruby to parse through XML easily to query and find certain tag values?

I am working with an API and want to know how I can easily search and display/format the output based on the tags.
For example, here is the page with the API and examples of the XML OUtput:
http://developer.linkedin.com/docs/DOC-1191
I want to be able to treat each record as an object, such as User.first-name User.last-name so that I can display and store information, and do searches.
Is there perhaps a gem that makes this easier to do?
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<people-search>
<people total="108" count="10" start="0">
<person>
<id>tePXJ3SX1o</id>
<first-name>Bill</first-name>
<last-name>Doe</last-name>
<headline>Marketing Professional and Matchmaker</headline>
<picture-url>http://media.linkedin.com:/....</picture-url>
</person>
<person>
<id>pcfBxmL_Vv</id>
<first-name>Ed</first-name>
<last-name>Harris</last-name>
<headline>Chief Executive Officer</headline>
</person>
...
</people>
<num-results>108</num-results>
</people-search>
This might give you a jump start:
#!/usr/bin/env ruby
require 'nokogiri'
XML = %{<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<people-search>
<people total="108" count="10" start="0">
<person>
<id>tePXJ3SX1o</id>
<first-name>Bill</first-name>
<last-name>Doe</last-name>
<headline>Marketing Professional and Matchmaker</headline>
<picture-url>http://media.linkedin.com:/foo.png</picture-url>
</person>
<person>
<id>pcfBxmL_Vv</id>
<first-name>Ed</first-name>
<last-name>Harris</last-name>
<headline>Chief Executive Officer</headline>
</person>
</people>
<num-results>108</num-results>
</people-search>}
doc = Nokogiri::XML(XML)
doc.search('//person').each do |person|
firstname = person.at('first-name').text
puts "firstname: #{firstname}"
end
# >> firstname: Bill
# >> firstname: Ed
The idea is you're looping over the section that repeats, "person", in this case. Then you pick out the sections you want and extract the text. I'm using Nokogiri's .at() to get the first occurrence, but there are other ways to do it.
The Nokogiri site has good examples and well written documentation so be sure to spend a bit of time going over it. You should find it easy going.
nokogiri is a really nice xml parser for ruby that allows you to use xpath or css3 selectors to access your xml, but its not an xml to object mapper
there is a project called xml-mapping that does exactly this, by defining xpath expressions that should be mapped to object properties - and vice versa.
This is how I did it for the Ruby Challenge using the built-in REXML.
This is basicaly the parsing code for the whole document:
doc = REXML::Document.new File.new cia_file
doc.elements.each('cia/continent') { |e| #continents.push Continent.new(e) }
doc.elements.each('cia/country') { |e| #countries.push Country.new(self, e) }
http://nokogiri.org/ is an option you should investigate

Resources