REXML get specific element

P

Pierre Pat

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.
 
P

Pierre Pat

Obviously, in the second example, it's 'programmer' instead of
'magician' ;-)
(Copy paste failure in order to leave some magician's privacy :p)
 
R

Robert Klemme

2009/3/18 Pierre Pat said:
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}]"]

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
 
H

Henrik Hodne

Hello,


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...

You are missing the '-s.
Try
doc.root.elements["programmer[@name='#{entered_name}']"]
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.

Regards,
Henrik Hodne
 
P

Phlip

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)
 
7

7stud --

Phlip said:
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:

1)
<xml version="1.0" encoding="ISO-8859-1"?>
<programmers>
<programmer age="50">John</programmer>
<programmer age="30">Sally</programmer>
</programmers>


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"?>
<programmers>
<programmer age="50">Joh'n</programmer>
<programmer age="30">Sally</programmer>
</programmers>


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?
 
P

Phlip

7stud said:
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 \.
 
7

7stud --

Phlip said:
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"?>
<programmers>
<programmer age="50">Joh'n</programmer>
<programmer age="30">Sally</programmer>
</programmers>


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:

<programmer age="50">John</programmer>
<programmer age="50">Joh'n</programmer>
<programmer age="50">Joh"n</programmer>

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

7stud --

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"?>
<programmers>
<programmer age="50">Joh'n</programmer>
<programmer age="30">John</programmer>
<programmer age="20">Joh"n</programmer>
</programmers>


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 method `node_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:in
`internal_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:in `Predicate'
from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:346:in `Predicate'
from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:204:in
`internal_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:in
`internal_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:in `parse'
from /usr/lib/ruby/1.8/rexml/xpath.rb:59:in `match'
from r1test.rb:11
 
M

Matthias Reitinger

7stud said:
This doesn't work for me:

<xml version="1.0" encoding="ISO-8859-1"?>
<programmers>
<programmer age="50">Joh'n</programmer>
<programmer age="30">Sally</programmer>
</programmers>


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"?>
<programmers>
<programmer age="50">John</programmer>
<programmer age="50">Joh'n</programmer>
<programmer age="50">Joh"n</programmer>
</programmers>


-Matthias
 
P

Pierre Pat

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

It really helped!
Thanks again
 
7

7stud --

Matthias said:
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={})
 
P

Pierre Pat

Matthias said:
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 :eek:

Is there any way to do that?

Regards
 
P

Pierre Pierre

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 :)
 
P

Phlip

7stud said:
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'".
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top