Creating New Objects from REXML::Elements

Hello group,

I'm new to using REXML to parse documents, but quickly getting the hang
of it. Something, however, is giving me a little trouble, so I could
use some help. I'm trying to create a new object, specifically a Movie
from elements in Delicious Library's XML data file (a Mac program,
found at http://www.delicious-monster.com). Basically, the XML file
stores the good stuff in tag attributes, and I'd rather make a new
objects from the attributes. I can pull the attributes, and puts proves
I'm pulling right, but when I try to push my new objects into an array,
the class identifies itself as an REXML::Element instead of a Movie.

This is the method that puts the new Movies into the array.
def get_movies
    temp = @library.root
    temp.elements.each('items/movie') { |movie|
        @movies.push(Movie.new(movie.attributes['uuid'],
movie.attributes['fullTitle'], movie.attributes['asin'],
movie.attributes['mpaarating'], movie.attributes['minutes'],
movie.attributes['published'], movie.attributes['price']))
        puts movie.attributes['fullTitle']
      }
end

But, irb tells me this:
irb(main):025:0> movies.class
=> Array
irb(main):026:0> movies[1].class
=> REXML::Element

Am I doing something completely wrong? Shouldn't the values of the
attributes be passed directly to the constructor and I get a new object
of class Movie?

Thanks for the help,
Eddie

eddieroger wrote:

Hello group,

I'm new to using REXML to parse documents, but quickly getting the hang
of it. Something, however, is giving me a little trouble, so I could
use some help. I'm trying to create a new object, specifically a Movie
from elements in Delicious Library's XML data file (a Mac program,
found at http://www.delicious-monster.com). Basically, the XML file
stores the good stuff in tag attributes, and I'd rather make a new
objects from the attributes. I can pull the attributes, and puts proves
I'm pulling right, but when I try to push my new objects into an array,
the class identifies itself as an REXML::Element instead of a Movie.

This is the method that puts the new Movies into the array.
def get_movies
    temp = @library.root
    temp.elements.each('items/movie') { |movie|
        @movies.push(Movie.new(movie.attributes['uuid'],
movie.attributes['fullTitle'], movie.attributes['asin'],
movie.attributes['mpaarating'], movie.attributes['minutes'],
movie.attributes['published'], movie.attributes['price']))
        puts movie.attributes['fullTitle']
      }
end

Can you send the full code? Are you sure that e.g. Movie.new does not return a REXML::Element for example? Anyway, I can not really figure this out without seeing the full code.

Cheers,
Peter

···

__
http://www.rubyrailways.com

I've encountered precisely this same problem! I use REXML to pull values out of an XML document and into an object; but, instead of my objects, I end up with an Array of REXML::Element's. :frowning:

TomP

···

On Dec 21, 2006, at 1:40 AM, eddieroger wrote:

[...] when I try to push my new objects into an array,
the class identifies itself as an REXML::Element instead of a Movie.
[...]
irb tells me this:
irb(main):025:0> movies.class
=> Array
irb(main):026:0> movies[1].class
=> REXML::Element

Can you send the full code? Are you sure that e.g. Movie.new does not return a REXML::Element for example? Anyway, I can not really figure this out without seeing the full code.

And the XML as well.

Cheers,
Bob

···

On 21-Dec-06, at 3:05 AM, Peter Szinek wrote:

Cheers,
Peter

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

Have you solved this? A fancy workaround, perhaps? Short of actually
making strings equivalent to the pieces, I am running out of ideas,
except the inefficiency of that kills me. I guess I'll give that a shot
and post an update if I get it to work.

Tom Pollard wrote:

···

On Dec 21, 2006, at 1:40 AM, eddieroger wrote:
> [...] when I try to push my new objects into an array,
> the class identifies itself as an REXML::Element instead of a Movie.
> [...]
> irb tells me this:
> irb(main):025:0> movies.class
> => Array
> irb(main):026:0> movies[1].class
> => REXML::Element

I've encountered precisely this same problem! I use REXML to pull
values out of an XML document and into an object; but, instead of my
objects, I end up with an Array of REXML::Element's. :frowning:

TomP

Here is the full Ruby code:

require 'rexml/document'
include REXML

class DeliciousLibrary

  #usual_path is the path of the DL XML file (until v2). Unless this
script is on a server, this works.
  @@usual_path = "#{ENV['HOME']}/Library/Application Support/Delicious
Library/Library Media Data.xml"

  @@medium_covers = "#{ENV['HOME']}/Library/Application
Support/Delicious Library/Medium Covers/" #requires UUID for
identification

  def initialize(file = @@usual_path)
    @library = Document.new(File.new(file))
    @movies = Array.new
  end

  def get_library_as_xml
    @library.root
  end

  def get_movies
    temp = @library.root
    temp.elements.each('items/movie') { |movie|
        @movies.push(Movie.new(movie.attributes['uuid'].to_str,
movie.attributes['fullTitle'], movie.attributes['asin'],
movie.attributes['mpaarating'], movie.attributes['minutes'],
movie.attributes['published'], movie.attributes['price']))
        puts movie.attributes['fullTitle']
      }
  end

  def get_shelves
    @library.root.elements['items/shelves']
  end

end

class Movie
  #Some Supplimental Classes
  def initialize(uuid, fullTitle, asin, mpaarating, minutes, published,
price)
    @uuid = uuid
    @fullTitle = fullTitle
    @asin = asin
    @mpaarating = mpaarating
    @minutes = minutes
    @published = published
    @price = price
  end
  attr_reader :uuid, :fullTitle, :asin, :mpaarating, :minutes,
:published, :price
end

The XML file is about 7000 lines long, os here's a snippet. i don't
think you really want 7000 lines of code listed anyway.

<movie asin="B000BW7QWW" aspect="DVD" country="us" created="188189008"
currentValue="$9.96"
features="AC-3&#x000a;Animated&#x000a;Color&#x000a;Dolby&#x000a;Dubbed&#x000a;Subtitled&#x000a;Widescreen&#x000a;NTSC&#x000a;2.35:1"
fullTitle="Serenity (Widescreen Edition)" genre="Sci-Fi
Action&#x000a;Futuristic&#x000a;Space Adventure&#x000a;Action &amp;
Adventure&#x000a;Science Fiction &amp; Fantasy"
lastLookupTime="188189024" minutes="119" mpaarating="PG-13"
netrating="4.5" price="$19.98" published="20-12-2005"
publisher="Universal Studios Home Entertainment"
purchaseDate="18-12-2006" stars="Chiwetel Ejiofor&#x000a;Nathan
Fillion&#x000a;Gina Torres&#x000a;Morena Baccarin&#x000a;Adam
Baldwin&#x000a;Raphael Feldman&#x000a;Yan Feldman&#x000a;Ron
Glass&#x000a;Summer Glau&#x000a;Michael Hitchcock&#x000a;Glenn
Howerton&#x000a;David Krumholtz&#x000a;Sean Maher&#x000a;Sarah
Paulson&#x000a;Nectar Rose&#x000a;Jewel Staite&#x000a;Tamara
Taylor&#x000a;Alan Tudyk&#x000a;Hunter Ansley Wryn"
theatricalDate="30-09-2005" title="Serenity" upc="0025192632723"
uuid="0CEA2670-C438-4C35-95A6-35F2E21DD05E">
...
</movie>

Thanks for the help.

Bob Hutchison wrote:

···

On 21-Dec-06, at 3:05 AM, Peter Szinek wrote:

> Can you send the full code? Are you sure that e.g. Movie.new does
> not return a REXML::Element for example? Anyway, I can not really
> figure this out without seeing the full code.
>

And the XML as well.

Cheers,
Bob

> Cheers,
> Peter

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

No, not yet. I'll try to find some more time to play with it (and try Bob's suggestion) this evening.

TomP

···

On Dec 22, 2006, at 1:20 AM, eddieroger wrote:

Have you solved this?

Finally. I realized I'd made the bone-headed mistake of not specifying an explicit return value in my function that processed the REXML fragment. What I had was

class Evcase
     def from_rexml ( record )
         record.each_element do |el|
           begin
             self.send("ev_#{el.name.downcase}=", el.text)
           rescue
             Evcase.create_accessors(el.name.downcase)
             retry
           end
         end
     end
end

This was used from code that looked like

doc.root.get_elements('PROBLEM_RECORD').each do |rec|
     @cases << Evcase.new.from_rexml(rec)
end

This latter code depends on the Evcase object being returned from the _from_rexml method; instead, it was returning the REXML element ('record') that had been passed in. I'm not sure why it took me so long to see that. Hope this helps someone else...

TomP

···

On Dec 22, 2006, at 1:20 AM, eddieroger wrote:

Tom Pollard wrote:

On Dec 21, 2006, at 1:40 AM, eddieroger wrote:

[...] when I try to push my new objects into an array,
the class identifies itself as an REXML::Element instead of a Movie.
[...]
irb tells me this:
irb(main):025:0> movies.class
=> Array
irb(main):026:0> movies[1].class
=> REXML::Element

I've encountered precisely this same problem! I use REXML to pull
values out of an XML document and into an object; but, instead of my
objects, I end up with an Array of REXML::Element's. :frowning:

Have you solved this?

Have you tried to_str on *all* of the arguments?

Cheers,
Bob

···

On 21-Dec-06, at 9:25 AM, eddieroger wrote:

  def get_movies
    temp = @library.root
    temp.elements.each('items/movie') { |movie|
        @movies.push(Movie.new(movie.attributes['uuid'].to_str,
movie.attributes['fullTitle'], movie.attributes['asin'],
movie.attributes['mpaarating'], movie.attributes['minutes'],
movie.attributes['published'], movie.attributes['price']))
        puts movie.attributes['fullTitle']
      }
  end

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

Not exactly what I did right, but I managed to get it. Basically, its
the same as before, but I replaced my get_movies and get_library with
this:

  def get_library
    @library
  end

  def get_movies
    allmovies = Array.new
    temp = get_library
    temp = temp.root
    temp.elements.each('items/movie') { |movie|
        tempm = Movie.new(movie.attributes['uuid'].to_s,
movie.attributes['title'].to_s, movie.attributes['asin'].to_s,
movie.attributes['mpaarating'].to_s, movie.attributes['minutes'].to_s,
movie.attributes['published'].to_s, movie.attributes['price'].to_s)
        allmovies << tempm
      }
    return allmovies
  end

So, @movies isn't initialized any more, and I just return the array of
Movies to irb or whatever calls it. I realized that I needed it there
more anyway, and if i really want it automatically created, I'll just
set @movies = get_movies in the constructor. Also, notice the .to_s
after each attribute. I'm not sure if its necessary, but that's just
how it is for now. Thanks for the help, all.

Eddie