Empact / roxml

ROXML is a module for binding Ruby classes to XML. It supports custom mapping and bidirectional marshalling between Ruby and XML using annotation-style class methods, via Nokogiri or LibXML.
http://roxml.rubyforge.org/
MIT License
223 stars 176 forks source link

Can't have an array with plural names ? #17

Closed jlecour closed 14 years ago

jlecour commented 14 years ago

Hi, I'm consuming a WebService that gives me some weird XML. I have something like

    <root_node>
      <some_node>
        <books><id>5</id><name>Book 5</name></books>
        <books><id>9</id><name>Book 9</name></books>
      </some_node>
    </root_node>

I'd like to have an array of "books", but it seems impossible (as far as I can tell) because the "books" node name is already a plural. I know, I should be singular in the first place, but I don't control this, it comes from a major player in the tourism industry.

I've tried many things, including specifying an explicit :from, … but it doesn't work. If I create a copy of the XML and change "books" into a singular node, it's working great.

Any idea on how I can do this ?

Empact commented 14 years ago

I don't believe I've tested this case. If you'd write a failing test case and push it out, I'd find it very helfull, but in the mean time, have you taken a look at the :from option? When you explicitly specify what node you'd like to query from, roxml should always honor that. In short: xml_reader :books, :as => SomeIdAndNameObject, :from => 'books', :in => 'some_node'

I can't give any guarantees, but that's a likely fix.

jlecour commented 14 years ago

Hi, thanks for your answer. I've tried what you suggest and it doesn't work.

Here is the exact code I ran :

require 'rubygems'
require 'roxml'
require 'pp'

xml = %(
<myxml>
  <store>
    <books><id>1</id><title>first book</title></books>
    <books><id>2</id><title>second book</title></books>
    <books><id>3</id><title>third book</title></books>
  </store>
</myxml>
)

class Book
  include ROXML
  xml_reader :id, :as => Integer
  xml_reader :title
end

class Store
  include ROXML
  xml_reader :books, :from => 'books', :in => 'store', :as => [Book]
end

class MyXml
  include ROXML
  xml_reader :store, :as => Store
end

pp MyXml.from_xml(xml).store.books

About the failing test, I'm still in the learning process of writing tests and I'm not familiar with the way you organized your specs. I you tell me which spec file I should write my test in, I'd try to make one and send you a pull request.

Cheers

findchris commented 14 years ago

+1

I can't get ROXML to parse this:

    <activity>
      <id>88</id>
      <recent-comment-engagement-datas>
        <comment-engagement-data>
          <label>test label</label>
          <body>test body</body>
          <ago>30 minutes ago</ago>
        </comment-engagement-data>
      </recent-comment-engagement-datas>
    </activity>

My Activity class (using the dasherize xml_convention) defines this:

xml_accessor :comment_engagement_datas, :as => [CommentEngagementData], :from => "comment-engagement-data", :in => "recent-comment-engagement-datas"

I believe it is looking for _datum instead of _datas (the ROXML::Definition @name is 'comment-engagement-datum'): Can I override this somehow?

Thanks.

findchris commented 14 years ago

I verified my suspicion by making the field required and getting this: ROXML::RequiredElementMissing: comment-engagement-datum from

Hope that helps.

Empact commented 14 years ago

Hi findchris, this has to do with ActiveSupport's pluralization, which you can override in the same way you would in rails, by establishing inflection rules: http://as.rubyonrails.org/classes/ActiveSupport/Inflector/Inflections.html Normally this would happen in a rails initializer, but you should be able to do it in a lib after you require roxml just as well.

findchris commented 14 years ago

@Empact I added a rule like:

ActiveSupport::Inflector.inflections do |inflect|
  inflect.irregular 'data', 'datas'
end

I don't think that worked. However, using this line seemed to do the trick:

xml_accessor :comment_engagement_datas, :as => [CommentEngagementData], :in => "recent-comment-engagement-datas"

I guess I didn't need the ':from' option.

Thanks for your reply and for ROXML :-)

-Chris

konung commented 14 years ago

Thank you Chris . I had exactly the same problem with taxes and Tax.....

vihai commented 14 years ago

The problem is that the singularization is made after the assignment from :from

  @name = (opts[:from] || @attr_name).to_s
  @name = @name.singularize if hash? || array?

Maybe the proper fix would be to apply "signularize" only to @attr_name

findchris commented 14 years ago

@vihai Good find. Fork and pull request?

vihai commented 14 years ago

I have forked and committed a patch but I'm not yet sure how this whole git-stuff works...

Let me try to send a pull request but I cannot guarantee the results :)

findchris commented 14 years ago

I applied it to my branch. Maybe @Empact will do the same.

Empact commented 14 years ago

Alright, I applied the fix and speced it using jlecour's example in spec/xml/array_spec.rb Note there was a problem in the original fix that it wouldn't stringify symbolic :from, which is fixed in my master.

So, this should be fixed. Thanks guys!

jlecour commented 14 years ago

Thanks guys for this fix. I can't confirm yet that it's OK for my original situation, but I'll do it as soon as I can.