REXML get specific element


#1

All,

I’m trying for the first time to play with XML and Ruby.
Since I have only the core library at the moment, my choice went
directly for REXML.

So, here is my problem.
I’m willing to get a specific element in my xml file.

Normally I would do something like this:
doc.root.elements[“programmer[@name=‘Matz’]”]

But let’s say I have a user interface, and I let a user enter the
programmer he wants to see. How can I look for an element using a
variable?
I hoped something like this would work:
entered_name = ‘Matz’
doc.root.elements[“magician[@name=#{entered_name}]”]
Unfortunately, it didn’t…

After trying a few things, check with uncle google and in this forum,
I’m driving out of clues, so I’d appreciate if someone could show me the
correct way!

Thanks.


#2

Obviously, in the second example, it’s ‘programmer’ instead of
‘magician’ :wink:
(Copy paste failure in order to leave some magician’s privacy :p)


#3

2009/3/18 Pierre P. removed_email_address@domain.invalid:

doc.root.elements[“programmer[@name=‘Matz’]”]

But let’s say I have a user interface, and I let a user enter the
programmer he wants to see. How can I look for an element using a
variable?
I hoped something like this would work:
entered_name = ‘Matz’
doc.root.elements[“magician[@name=#{entered_name}]”]

You forgot the single quotes around #{}, i.e. you must only replace
“Matz” with “#{entered_name}”:

doc.root.elements[“programmer[@name=’#{entered_name}’]”]

Unfortunately, it didn’t…

After trying a few things, check with uncle google and in this forum,
I’m driving out of clues, so I’d appreciate if someone could show me the
correct way!

Cheers

robert


#4

Hello,

On 18 Mar 2009, at 06:15, Pierre P. removed_email_address@domain.invalid wrote:

doc.root.elements[“programmer[@name=‘Matz’]”]

But let’s say I have a user interface, and I let a user enter the
programmer he wants to see. How can I look for an element using a
variable?
I hoped something like this would work:
entered_name = ‘Matz’
doc.root.elements[“magician[@name=#{entered_name}]”]
Unfortunately, it didn’t…

You are missing the ‘-s.
Try
doc.root.elements["programmer[@name=’#{entered_name}’]"]

Regards,
Henrik H.


#5

You are missing the ‘-s.
Try
doc.root.elements["programmer[@name=’#{entered_name}’]"]

Then that croaks if the name contains an apostrophe.

Try (IIRC)…

XPath.first(doc, ‘//programmer[ @name = $name ]’, :name =>
entered_name)


#6

Phlip wrote:

You are missing the ‘-s.
Try
doc.root.elements["programmer[@name=’#{entered_name}’]"]

Then that croaks if the name contains an apostrophe.

Try (IIRC)…

XPath.first(doc, ‘//programmer[ @name = $name ]’, :name =>
entered_name)

That doesn’t work for me. And I couldn’t figure out the escaping
necessary to handle both a name entered with a single quote or a double
quote. I thought I understood ruby escaping, but not anymore.

Here is the easy case:

<xml version=“1.0” encoding=“ISO-8859-1”?>

John
Sally

require ‘rexml/document’
include REXML

f = File.new(‘xml4.xml’)
doc = Document.new(f)

input_name = gets
input_name = input_name.chomp

target = XPath.match(doc, “//programmer[text() = ‘John’]”)
puts target[0].attributes[“age”]


$ ruby r1test.rb
John
50

Now suppose the programmer’s name is Joh’n

<xml version=“1.0” encoding=“ISO-8859-1”?>

Joh’n
Sally

This is one of my favorite attempts to escape a single quote in the
input name:

input_name = “Joh’n”
p input_name
input_name = input_name.gsub("’", “\’”)
p input_name

–output:–
“Joh’n”
“Johnn”

Can anyone explain what’s going on there?


#7

Phlip wrote:

7stud – wrote:

That doesn’t work for me. And I couldn’t figure out the escaping
necessary to handle both a name entered with a single quote or a double
quote. I thought I understood ruby escaping, but not anymore.

You understand Ruby escaping - just not XPath escaping. There is none.

Ah. Ok.

If you
use ’ to delimit a string, you cannot use ’ inside it. I would love to
be proven
wrong, but I did research this for quite a while.

Why didn’t my $name substitution work? It’s in the rdocs…

f = File.new(‘xml4.xml’)
doc = Document.new(f)

input_name = gets
input_name = input_name.chomp

target = XPath.match(doc, “//programmer[text() = ‘John’]”)
puts target[0].attributes[“age”]

Wouldn’t this work?

target = XPath.match(doc, “//programmer[text() = $name]”, :name =>
$john)

I don’t know what you are trying to do there. What is $john? This
doesn’t work for me:

<xml version=“1.0” encoding=“ISO-8859-1”?>

Joh’n
Sally

require ‘rexml/document’
include REXML

f = File.new(‘xml4.xml’)
doc = Document.new(f)

input_name = gets
input_name = input_name.chomp
p input_name

target = XPath.match(doc, “//programmer[text() = $name]”, :name =>
$input_name)
p target

puts target[0].attributes[“age”]

–output:–
$ ruby r1test.rb
Joh’n
“Joh’n”
[]
r1test.rb:14: undefined method `attributes’ for nil:NilClass
(NoMethodError)

I thought the question you presented was how are you going to be able to
handle all these cases in the xml:

John
Joh’n
Joh"n

The user may type in: John, or Joh’n, or Joh"n.


#8

7stud – wrote:

That doesn’t work for me. And I couldn’t figure out the escaping
necessary to handle both a name entered with a single quote or a double
quote. I thought I understood ruby escaping, but not anymore.

You understand Ruby escaping - just not XPath escaping. There is none.
If you
use ’ to delimit a string, you cannot use ’ inside it. I would love to
be proven
wrong, but I did research this for quite a while.

Why didn’t my $name substitution work? It’s in the rdocs…

f = File.new(‘xml4.xml’)
doc = Document.new(f)

input_name = gets
input_name = input_name.chomp

target = XPath.match(doc, “//programmer[text() = ‘John’]”)
puts target[0].attributes[“age”]

Wouldn’t this work?

target = XPath.match(doc, “//programmer[text() = $name]”, :name =>
$john)

input_name = input_name.gsub("’", “\’”)
p input_name

–output:–
“Joh’n”
“Johnn”

Can anyone explain what’s going on there?

XPath does not escape ticks with .


#9

The following code works when the user types in John or Joh’n but not
Joh"n:

<xml version=“1.0” encoding=“ISO-8859-1”?>

Joh’n
John
Joh"n

require ‘rexml/document’
include REXML

f = File.new(‘xml4.xml’)
doc = Document.new(f)

input_name = gets
input_name = input_name.chomp
p input_name

target = XPath.match(doc, “//programmer[text() = “#{input_name}”]”)
p target

puts target[0].attributes[“age”]

$ruby r1test.rb
Joh"n
“Joh"n”
/usr/lib/ruby/1.8/rexml/xpath_parser.rb:124:in internal_parse': undefined methodnode_type’ for “Joh”:String (NoMethodError)
from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:123:in each' from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:123:ininternal_parse’
from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:49:in match' from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:402:inPredicate’
from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:346:in Predicate' from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:204:ininternal_parse’
from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:199:in times' from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:199:ininternal_parse’
… 9 levels…
from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:49:in match' from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:34:inparse’
from /usr/lib/ruby/1.8/rexml/xpath.rb:59:in `match’
from r1test.rb:11


#10

7stud – wrote:

This doesn’t work for me:

<xml version=“1.0” encoding=“ISO-8859-1”?>

Joh’n
Sally

require ‘rexml/document’
include REXML

f = File.new(‘xml4.xml’)
doc = Document.new(f)

input_name = gets
input_name = input_name.chomp
p input_name

target = XPath.match(doc, “//programmer[text() = $name]”, :name =>
$input_name)
p target

puts target[0].attributes[“age”]

There are several flaws in your code: First, $input_name is not the
same variable as input_name. Second, according to the REXML
documentation the variables hash for the XPath query is in fact the
fourth argument to XPath#match, not the third. The third one is for the
namespaces hash. And lastly, you have to use strings as keys here, not
symbols (took me some time to figure that one out). So this does the
trick:

require ‘rexml/document’
include REXML

doc = Document.new(DATA)

input_name = gets.chomp
p input_name

target = XPath.match(doc, “//programmer[text()=$name]”, {}, “name” =>
input_name)
p target

puts target[0].attributes[“age”]

END
<xml version=“1.0” encoding=“ISO-8859-1”?>

John
Joh’n
Joh"n

-Matthias


#11

Thanks guys!
I knew it had to be something small, just couldn’t figure it out.

It really helped!
Thanks again


#12

Matthias R. wrote:

So this does the
trick:

target = XPath.match(doc, “//programmer[text()=$name]”, {}, “name” =>
input_name)

Nice. I downloaded the REXML docs and took a look around. A question:
how in the hell would anyone know to use a $ variable in there, and that
a hash specified as the 4th argument would somehow match a value to that
variable, when all the docs say is:

match(element, path=nil, namespaces=nil, variables={})


#13

Ha, actually, I hadn’t worked with XPath before, so I didn’t really know
what it was about.
It’s actually pretty cool, you can access any elements directly.
for instance:
the_lang = ‘ruby’
doc.elements["//language[@lang=’#{the_lang}’]"]
for an xml which would have a language tag inside the programmer one
with lang as attributes.

Learning and enjoying :slight_smile:


#14

Matthias R. wrote:

target = XPath.match(doc, “//programmer[text()=$name]”, {}, “name” =>
input_name)
p target

Since I’m now digging one level deeper in my xml and again trying to
locate a specific element, I thought I’d try XPath, which should be
easier.

But that example you gave is to locate an element knowing the text
value, then showing the attribute.
I’m trying to do the opposite, looking for an element depending on its
attribute’s value.

I looked in XPath and the Functions module it’s using, I can’t find any
method to do that :o

Is there any way to do that?

Regards


#15

7stud – wrote:

target = XPath.match(doc, “//programmer[text() = $name]”, :name =>
$john)

I don’t know what you are trying to do there. What is $john?

It is a typo.

This
doesn’t work for me:

target = XPath.match(doc, “//programmer[text() = $name]”, :name =>
$input_name)

Take the $ off. Sorry, but $ is also valid Ruby, meaning “a global
variable, yet
different from ‘input_name’”.