Humanized Xml tree navigation

Does anybody know of a library that allows to go through an xml tree as if
it were made of normal objects?

I have seen e4x on rubyforge, but it doesn't seem to work. simplexml returns
hashes rather than employing dynamic method calls

What I need to do is to be able to traverse an xml document as in:

* people.each_person {...}
* people.person[2].name
* people.person.name #takes the first person
* people.person.size

etc..

Any suggestion?

--kia

chiaro scuro wrote:

Does anybody know of a library that allows to go through an xml tree as if
it were made of normal objects?

I have seen e4x on rubyforge, but it doesn't seem to work. simplexml returns
hashes rather than employing dynamic method calls

What I need to do is to be able to traverse an xml document as in:

* people.each_person {...}
* people.person[2].name
* people.person.name #takes the first person
* people.person.size

etc..

Any suggestion?

I like the idea, but I'm unaware that it's been done
to that level.

Why don't you start with REXML and put a wrapper on it?
Then publish it for everyone to enjoy.

Or do you find REXML too slow for your use?

Hal

My xmlcodec project will do this but you'll need to write some simple
classes to do it. For this case, something like:

class PeopleFormat < XMLElement
  xmlformat 'People Format'
end

class People < PeopleFormat
  elname :people
  xmlsubel_mult :person
end

class Person < PeopleFormat
  elname :person
  xmlattr :name
  xmlattr :size
end

With this you can do:

people = PeopleFormat.import_xml_text(myXMLTextOrFile)
people.person.each {|p| ...}
people.person.first.name
people.person[2].type

You can find it at http://xmlcodec.rubyforge.org

Cheers,

Pedro.

···

On 7/31/06, chiaro scuro <kiaroskuro@gmail.com> wrote:

Does anybody know of a library that allows to go through an xml tree as if
it were made of normal objects?

I have seen e4x on rubyforge, but it doesn't seem to work. simplexml returns
hashes rather than employing dynamic method calls

What I need to do is to be able to traverse an xml document as in:

* people.each_person {...}
* people.person[2].name
* people.person.name #takes the first person
* people.person.size

Look here:
<http://code.whytheluckystiff.net/hpricot/>

or here:
<http://cvs.m17n.org/~akr/htree/>

···

On Jul 31, 2006, at 6:16 AM, chiaro scuro wrote:

Does anybody know of a library that allows to go through an xml tree as if
it were made of normal objects?

I have seen e4x on rubyforge, but it doesn't seem to work. simplexml returns
hashes rather than employing dynamic method calls

What I need to do is to be able to traverse an xml document as in:

* people.each_person {...}
* people.person[2].name
* people.person.name #takes the first person
* people.person.size

etc..

Any suggestion?

--
And those who were seen dancing were thought to be insane by those who could not hear the music.
-Friedrich Wilhelm Nietzsche, philosopher (1844-1900)

Hi,

chiaro scuro wrote:

Does anybody know of a library that allows to go through an xml tree as if
it were made of normal objects?

What I need to do is to be able to traverse an xml document as in:

* people.each_person {...}
* people.person[2].name
* people.person.name #takes the first person
* people.person.size

0% cat people.xml
<people>
  <person>
    <name>name1</name>
    <size>1</size>
  </person>
  <person>
    <name>name2</name>
    <size>2</size>
  </person>
  <person>
    <name>name3</name>
    <size>3</size>
  </person>
</people>
0% irb
irb(main):001:0> require 'xsd/mapping'
=> true
irb(main):002:0> people = XSD::Mapping.xml2obj(File.read("people.xml"))
=> #<SOAP::Mapping::Object:0x3592020
{}person=[#<SOAP::Mapping::Object:0x3591ea0 {}name="name1" {}size="1">,
#<SOAP::Mapping::Object:0x3591684 {}name="name2" {}size="2">,
#<SOAP::Mapping::Object:0x3590f7c {}name="name3" {}size="3">]>
irb(main):003:0> people.person.each { |person| p person }
#<SOAP::Mapping::Object:0x3591ea0 {}name="name1" {}size="1">
#<SOAP::Mapping::Object:0x3591684 {}name="name2" {}size="2">
#<SOAP::Mapping::Object:0x3590f7c {}name="name3" {}size="3">
=> [#<SOAP::Mapping::Object:0x3591ea0 {}name="name1" {}size="1">,
#<SOAP::Mapping::Object:0x3591684 {}name="name2" {}size="2">,
#<SOAP::Mapping::Object:0x3590f7c {}name="name3" {}size="3">]
irb(main):004:0> people.person[2].name
=> "name3"
irb(main):005:0> people.person.name
NoMethodError: undefined method `name' for #<Array:0x6b22108>
        from (irb):5
irb(main):006:0>

Almost.

Regards,
// NaHi

Consider

<vocab-item>
   <first.form>foo</first.form>
   <second.form>föö</second.form>
</vocab-item>

That's legal XML. Ruby n00b question: can you work around problems like "vocab-item" and "first.form" in Ruby? -Tim

···

On Jul 31, 2006, at 6:16 AM, chiaro scuro wrote:

What I need to do is to be able to traverse an xml document as in:

* people.each_person {...}
* people.person[2].name
* people.person.name #takes the first person
* people.person.size

Does anybody know of a library that allows to go through an xml tree as if
it were made of normal objects?

Your sample code looks a lot like RubyfulSoup. Although originally intended for traversing HTML documents, it has an option for non-specific XML as well.

* people.each_person {...}
* people.person[2].name
* people.person.name #takes the first person
* people.person.size

RubyfulSoup would (I believe) look something like:

  people.find_all['person'].each
  people.person.name
  people.person.size

if the XML were like

  <people>
    <person>
      <name>Fred</name>
      <size>150 kg</size>
    </person>
    <person>
      <name>Sally</name>
    .
    </person>
  </people>

If instead your XML is like

  <people>
    <person name="Fred" size="150 kg"/>
    <person name="Sally" size="88 kg"/>
    .
  </people>

Then you'd be looking at

  people.find_all['person'].each
  people.person['name']
  people.person['size']

I don't remember what it does with XML tags with squiggles and dots.

···

On Jul 31, 2006, at 6:16, chiaro scuro wrote:

I'm experimenting with this XML2RB [1]. The idea is to generate
a DSL for a class of XML files (from a bunch of XML files).
Each XML file in this class can be translated to a Ruby script,
which can be executed with the DSL. Running a script actually
builds the tree by default, including the classes and methods
as you mentioned. Each class can optionally be enriched with
new functionality.

All examples are real, but I've not yet released any code.
Untill now, it's just an experiment.

The document will grow every now and then. It's not static.

gegroet,
Erik V. - http://www.erikveen.dds.nl/

[1] http://www.erikveen.dds.nl/xml2rb/index.html

That's really nice, I'd looked over that in the standard library. I've used
Roxml with success to achieve the same effect, but it requires more work.

Alex

···

On Monday 31 July 2006 14:43, NAKAMURA, Hiroshi wrote:

0% irb
irb(main):001:0> require 'xsd/mapping'
=> true
irb(main):002:0> people = XSD::Mapping.xml2obj(File.read("people.xml"))
=> #<SOAP::Mapping::Object:0x3592020
{}person=[#<SOAP::Mapping::Object:0x3591ea0 {}name="name1" {}size="1">,
#<SOAP::Mapping::Object:0x3591684 {}name="name2" {}size="2">,
#<SOAP::Mapping::Object:0x3590f7c {}name="name3" {}size="3">]>
irb(main):003:0> people.person.each { |person| p person }
#<SOAP::Mapping::Object:0x3591ea0 {}name="name1" {}size="1">
#<SOAP::Mapping::Object:0x3591684 {}name="name2" {}size="2">
#<SOAP::Mapping::Object:0x3590f7c {}name="name3" {}size="3">
=> [#<SOAP::Mapping::Object:0x3591ea0 {}name="name1" {}size="1">,
#<SOAP::Mapping::Object:0x3591684 {}name="name2" {}size="2">,
#<SOAP::Mapping::Object:0x3590f7c {}name="name3" {}size="3">]
irb(main):004:0> people.person[2].name
=> "name3"
irb(main):005:0> people.person.name
NoMethodError: undefined method `name' for #<Array:0x6b22108>
        from (irb):5
irb(main):006:0>

Almost.

Regards,
// NaHi

uhm.. I admit I didn't think of that. I guess I will have to write some
kind of name normalized/denormalizer taking care of dots and hyphens.

all hyphens and dots will become underscores, I guess, and I'll just assume
nobody will try to get the code to fail using both at the same time :slight_smile:
nobody will, right?

···

On 7/31/06, Tim Bray <tbray@textuality.com> wrote:

On Jul 31, 2006, at 6:16 AM, chiaro scuro wrote:

> What I need to do is to be able to traverse an xml document as in:
>
> * people.each_person {...}
> * people.person[2].name
> * people.person.name #takes the first person
> * people.person.size

Consider

<vocab-item>
   <first.form>foo</first.form>
   <second.form>föö</second.form>
</vocab-item>

That's legal XML. Ruby n00b question: can you work around problems
like "vocab-item" and "first.form" in Ruby? -Tim

--
Chiaroscuro
---
Liquid Development: http://liquiddevelopment.blogspot.com/

class Foo
   define_method("first.form") do
     "hi from first.form"
   end
end
Foo.new.send("first.form") # => "hi from first.form"

-- Daniel

···

On Jul 31, 2006, at 11:16 AM, Tim Bray wrote:

Consider

<vocab-item>
  <first.form>foo</first.form>
  <second.form>föö</second.form>
</vocab-item>

That's legal XML. Ruby n00b question: can you work around problems like "vocab-item" and "first.form" in Ruby?

Workaround is the word <g>

class A
   define_method("@+$%!") { puts "Snoopy talk!" }
end

a = A.new
a.send("@+$%!")

···

On Jul 31, 2006, at 11:16 AM, Tim Bray wrote:

On Jul 31, 2006, at 6:16 AM, chiaro scuro wrote:

What I need to do is to be able to traverse an xml document as in:

* people.each_person {...}
* people.person[2].name
* people.person.name #takes the first person
* people.person.size

Consider

<vocab-item>
  <first.form>foo</first.form>
  <second.form>föö</second.form>
</vocab-item>

That's legal XML. Ruby n00b question: can you work around problems like "vocab-item" and "first.form" in Ruby? -Tim

Tim Bray <tbray@textuality.com> writes:

···

On Jul 31, 2006, at 6:16 AM, chiaro scuro wrote:

What I need to do is to be able to traverse an xml document as in:

* people.each_person {...}
* people.person[2].name
* people.person.name #takes the first person
* people.person.size

Consider

<vocab-item>
  <first.form>foo</first.form>
  <second.form>föö</second.form>
</vocab-item>

That's legal XML. Ruby n00b question: can you work around problems
like "vocab-item" and "first.form" in Ruby? -Tim

Just make the API more flexible:

document["vocab-item"].bla["second.form"]

--
Christian Neukirchen <chneukirchen@gmail.com> http://chneukirchen.org

Hi,

Tim Bray wrote:

Consider

<vocab-item>
  <first.form>foo</first.form>
  <second.form>föö</second.form>
</vocab-item>

That's legal XML. Ruby n00b question: can you work around problems like
"vocab-item" and "first.form" in Ruby? -Tim

With xsd/mapping;

0% irb
irb(main):001:0> require 'xsd/mapping'
=> true
irb(main):002:0> obj = XSD::Mapping.xml2obj(<<XML)
irb(main):003:1" <obj>
irb(main):004:1" <vocab-item>
irb(main):005:1" <first.form>foo</first.form>
irb(main):006:1" <second.form>foo</second.form>
irb(main):007:1" </vocab-item>
irb(main):008:1" </obj>
irb(main):009:1" XML
=> #<SOAP::Mapping::Object:0x3591144
{}vocab-item=#<SOAP::Mapping::Object:0x3590f64 {}first.form="foo"
{}second.form="foo">>

irb(main):010:0> obj.methods - Kernel.methods
=> ["[]", "vocab_item=", "[]=", "__xmlattr", "__xmlele",
"__add_xmlele_value", "vocab_item"]

irb(main):011:0> obj["vocab-item"]
=> #<SOAP::Mapping::Object:0x3590f64 {}first.form="foo" {}second.form="foo">

irb(main):012:0> obj[XSD::QName.new(nil, "vocab-item")]
=> #<SOAP::Mapping::Object:0x3590f64 {}first.form="foo" {}second.form="foo">

Last line is for your next question, XML Namespace. And see "__xmlattr"
method listed for the next.

Regards,
// NaHi

In article <62e8012c0607310632hb44a0b6md149d6462d20130c@mail.gmail.com>,

···

Pedro Côrte-Real <pedro@pedrocr.net> wrote:

On 7/31/06, chiaro scuro <kiaroskuro@gmail.com> wrote:

Does anybody know of a library that allows to go through an xml tree as if
it were made of normal objects?

I have seen e4x on rubyforge, but it doesn't seem to work. simplexml returns
hashes rather than employing dynamic method calls

What I need to do is to be able to traverse an xml document as in:

* people.each_person {...}
* people.person[2].name
* people.person.name #takes the first person
* people.person.size

My xmlcodec project will do this but you'll need to write some simple
classes to do it. For this case, something like:

class PeopleFormat < XMLElement
xmlformat 'People Format'
end

class People < PeopleFormat
elname :people
xmlsubel_mult :person
end

class Person < PeopleFormat
elname :person
xmlattr :name
xmlattr :size
end

With this you can do:

people = PeopleFormat.import_xml_text(myXMLTextOrFile)
people.person.each {|p| ...}
people.person.first.name
people.person[2].type

You can find it at http://xmlcodec.rubyforge.org

Nice. I've done this sort of thing, but much more 'manually' than you
show here.

Phil

Hi,

What I need to do is to be able to traverse an xml document as in:

* people.each_person {...}
* people.person[2].name
* people.person.name #takes the first person
* people.person.size

Consider

<vocab-item>
  <first.form>foo</first.form>
  <second.form>föö</second.form>
</vocab-item>

That's legal XML. Ruby n00b question: can you work around problems like "vocab-item" and "first.form" in Ruby? -Tim

I feel *compelled* to mention xampl (<http://rubyforge.org/projects/xampl/>). Using the current version for ruby, the program looks like this:

thing = open("xml/funnystuff.xml"){ | f | XamplObject.from_xml_string f.read }
puts thing.pp_xml
puts thing.class.name
thing.first_form.first.content = 'hello there'
puts thing.pp_xml

The output like this:

<vocab-item>
   <first.form>foo</first.form>
   <second.form>föö</second.form></vocab-item>
XamplAdHoc::VocabItem

<vocab-item>
   <first.form>hello there</first.form>
   <second.form>föö</second.form></vocab-item>

The file xml/funnystuff.xml has Tim's example xml in it.

The XamplAdHoc stuff is there because there is no namespace declarations in the xml and I didn't supply a default mapping to a Ruby Module.

Xampl does a lot more than this (in particular it does a weak-transactional 'transparent' persistence kind of thing). It has been around in Java since the late 1990s (where it does 'real' transactions', CommonLisp for a couple of years, and Ruby for 18 months. I've written several programs well over 500 kloc using the Java version. Currently using xampl in place of ActiveRecord in a Rails project I'm working on (but you need the version I'm preparing to release for use with Rails, so please be patient).

Cheers,
Bob

···

On Jul 31, 2006, at 11:16 AM, Tim Bray wrote:

On Jul 31, 2006, at 6:16 AM, chiaro scuro wrote:

----
Bob Hutchison -- blogs at <http://www.recursive.ca/hutch/>
Recursive Design Inc. -- <http://www.recursive.ca/>
Raconteur -- <http://www.raconteur.info/>
xampl for Ruby -- <http://rubyforge.org/projects/xampl/>

Hi,

0% cat people.xml
<people>
  <person>
    <name>name1</name>
    <size>1</size>
  </person>
  <person>
    <name>name2</name>
    <size>2</size>
  </person>
  <person>
    <name>name3</name>
    <size>3</size>
  </person>
</people>
0% irb
irb(main):001:0> require 'xsd/mapping'
=> true
irb(main):002:0> people = XSD::Mapping.xml2obj(File.read("people.xml"))
=> #<SOAP::Mapping::Object:0x3592020
{}person=[#<SOAP::Mapping::Object:0x3591ea0 {}name="name1" {}size="1">,
#<SOAP::Mapping::Object:0x3591684 {}name="name2" {}size="2">,
#<SOAP::Mapping::Object:0x3590f7c {}name="name3" {}size="3">]>
irb(main):003:0> people.person.each { |person| p person }
#<SOAP::Mapping::Object:0x3591ea0 {}name="name1" {}size="1">
#<SOAP::Mapping::Object:0x3591684 {}name="name2" {}size="2">
#<SOAP::Mapping::Object:0x3590f7c {}name="name3" {}size="3">
=> [#<SOAP::Mapping::Object:0x3591ea0 {}name="name1" {}size="1">,
#<SOAP::Mapping::Object:0x3591684 {}name="name2" {}size="2">,
#<SOAP::Mapping::Object:0x3590f7c {}name="name3" {}size="3">]
irb(main):004:0> people.person[2].name
=> "name3"
irb(main):005:0> people.person.name
NoMethodError: undefined method `name' for #<Array:0x6b22108>
        from (irb):5
irb(main):006:0>

Almost.

Nice! I didn't know about this.

Loses child ordering though, mixed content, and the root element?

Both of the following lines:

puts XSD::Mapping.obj2xml(XSD::Mapping.xml2obj("<p><a/><b/><a/><b/></

"))

puts XSD::Mapping.obj2xml(XSD::Mapping.xml2obj("<p>1<a/>2<b/>3<a/>4<b/ >5</p>"))

give the same output:

<?xml version="1.0" encoding="utf-8" ?>
<SOAP..Mapping..Object>
   <a>
   </a>
   <a>
   </a>
   <b>
   </b>
   <b>
   </b>
</SOAP..Mapping..Object>

Cheers,
Bob

···

On Jul 31, 2006, at 9:43 AM, NAKAMURA, Hiroshi wrote:

----
Bob Hutchison -- blogs at <http://www.recursive.ca/hutch/>
Recursive Design Inc. -- <http://www.recursive.ca/>
Raconteur -- <http://www.raconteur.info/>
xampl for Ruby -- <http://rubyforge.org/projects/xampl/>

chiaro scuro wrote:
> Does anybody know of a library that allows to go through an xml tree as if
> it were made of normal objects?
>
> I have seen e4x on rubyforge, but it doesn't seem to work. simplexml
> returns
> hashes rather than employing dynamic method calls
>
> What I need to do is to be able to traverse an xml document as in:
>
> * people.each_person {...}
> * people.person[2].name
> * people.person.name #takes the first person
> * people.person.size
>
> etc..
>
> Any suggestion?

It's not really the best way to traverse XML becuase there's some
exceptions --keywords that ruby uses that wouldn't be traverable as
tags, and legal XML tags that you would not be able to use as method
calls.

I'm suprised the the e4x implemention on Rubyforge didn't work at all
--I'll have to look at it, but in any case it is completely
expiremental, and incomplete. For my needs I have moved on to a
Ruby-esque interface instead (http://cherry.rubyforge.org).

T.

in the meantime I wrote a little REXML wrapper, but I'll check the solutions
you guys sent me and see if I can throw away some code :slight_smile:

cheers,
Kia

Hi,

Bob Hutchison wrote:

Loses child ordering though, mixed content, and the root element?

Yes, Yes and Yes. obj2xml and xml2obj is not for round-trip conversion
although its name indicates it.

I introduced this XML <-> Ruby mapping to avoid bothersome XML DOM
programming in Web Services (soap4r). Do you think xampl can help us
instead of XSD::Mapping?

For example, flickr client is as follows;
(cf. http://dev.ctor.org/soap4r/browser/trunk/sample/soap/flickr.rb)

  flickr = SOAP::RPC::Driver.new('http://www.flickr.com/services/soap/')
  ...
  response = flickr.request(
    :api_key => api_key,
    :method => 'flickr.test.echo',
    :name => 'hello world')

  responsexml = "<dummy>#{response}</dummy>" # svr returns partial XML!
  require 'xsd/mapping'
  obj = XSD::Mapping.xml2obj(responsexml)
  p obj.method
  p obj.name

Creating XML request with a Hash and creating Ruby object with
XSD::Mapping. Both are for XML <-> Ruby mapping. It's enough small but
when I come to create more complexed XML instance... How I can write
this with xampl?

Regards,
// NaHi