Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

Preserve XML Attributes with Hash#from_xml and ActiveResource

#1598

Currently, Hash#to_xml and, by extension, ActiveResource ignore XML attributes in certain scenarios.

First, attributes are ignored on tags that have no children. For example:


Hash.from_xml("<tag attr='val'>content</tag>") #=> {"tag"=>"content"}

Unfortunately, in some instances, one does want to retain these attributes. This patch adds support for a second parameter for Hash.from_xml. This parameter, when set to true (default is false), preserves the attributes.


Hash.from_xml("<tag attr='val'>content</tag>", true) #=> {"tag"=>{"content"=>"content", "attr"=>"val"}}

The result is not quite as elegant, but is preferred to loss of data in some cases.

Furthermore, if the attribute is "type", Rails will by default attempt to typecast the content as such:


Hash.from_xml("<tag type='float'>1</tag>") #=> {"tag"=>1.0}

With the second parameter set to true, the normal behavior will still be retained where possible, but where no match can be found for the type, it will be passed through:


Hash.from_xml("<tag type='float'>1</tag>", true) #=> {"tag"=>1.0}
Hash.from_xml("<tag type='number'>1</tag>", true) #=> {"tag"=>{"type"=>"number", "content"=>"1"}}

More information about this problem and my solution is available in a writeup on my blog: Stop Hash.from_xml from Killing XML Attributes

Also included in this patch is a new format for ActiveResource called AttributePreservingXmlFormat in the event that you need ActiveResource to use the improved Hash.from_xml.

If anyone has a better idea about how to solve this problem, I'd love some feedback.

Reported by Peter Wagenet · December 18th, 2008 @ 06:28 PM

State: open
Milestone: 3.x
Assigned to: Mikel Lindsaar Mikel Lindsaar
Importance: Low

Activity

  1. Pratik
    Pratik
    • Assigned user set to Pratik
    • Tag changed from activeresource, format, from_xml, hash, patch, tested, xml to activeresource, format, from_xml, hash, improvement, patch, tested, xml

    Surely like the idea. I think it's better to use options hash for preseve_attributes. Something like :

    
    def from_xml(xml, options = {})
      preserve_attributes = options.delete(:preserve_attributes)
      ...
    

    March 10th, 2009 @ 11:27 AM

  2. Peter Wagenet
    Peter Wagenet

    So instead of taking a boolean take a hash that could also be used for other stuff later? That's not a bad idea. I'll see about changing this. I'll also check that it's all still compatible (and necessary) with 2.3.

    March 10th, 2009 @ 03:47 PM

  3. Pratik
    Pratik

    Thanks. I think it's probably a little late to push this for 2.3. But I'll commit as soon as the 2.3 stable release is out.

    March 10th, 2009 @ 04:13 PM

  4. Peter Wagenet
    Peter Wagenet

    Yeah, that's probably true. I'll take a look at 3.0 as well.

    March 13th, 2009 @ 12:29 AM

  5. Peter Wagenet
    Peter Wagenet

    Here's the Rails 2.3 version anyway.

    March 13th, 2009 @ 12:48 AM

  6. Peter Wagenet
    Peter Wagenet

    And here's the one for 3.0. The code is the same, but the patch wouldn't apply so I had to redo it.

    March 13th, 2009 @ 01:04 AM

  7. Nick Eskelinen
    Nick Eskelinen

    While I like the general idea of this, the "content" key is troubling. What I'd like to see is a parsing of XML that preserves the difference between tag attributes and tag bodies.

    In order to do this, it should separate the data into an (attributes, content) tuple.

    Examples:

    
    Hash.from_xml("<tag attr='val'>content</tag>", true) #=> {"tag"=> [{"attr" => "val"}, "content"]}
    
    Hash.from_xml("<tag content='inline' attr='val'>actual content</tag>") #=> {"tag" => [{"content" => "inline", "attr" => "val"}, "actual content"]}
    
    
    

    April 10th, 2009 @ 09:09 PM

  8. Mark Roach
    Mark Roach

    Nick: It would be nice to come up with a solution that couldn't possibly be generated by the current implementation. The tuple example you give fits that bill, but only in the case of an attribute. if "tag" were a nested resource instead, this would generate a list of hashes which already has a meaning.

    I don't love an idea that requires looking at the hash keys themselves, but I think it might be necessary. How about something like this:

    {"tag" => {:content => "actual content", :attributes => {"content" => "inline", "attr" => "val"} } }

    May 4th, 2009 @ 07:43 PM

  9. howardk
    howardk

    So where does this stand?

    This is a fundamental problem in Rails -- Hash.from_xml just isn't very XML savvy.

    Elements can have multiple types of children, but there's 3 notable ones:
    * attribute * element * #text e.g.
    23

    Now arguably you could state an Element -or- #text is handled, but not both. This matches with the notion a Hash key's value can be 'primitive'/atomic value (String/etc) or 'complex' (Hash) (or a list of either, i.e. Array).

    But the notion of only handling child Attributes if child Elements exist is seriously flawed.

    I'm not too fond of the key name (:content). Would be more accurate to call it :text or :value.
    Even better, to avoid collisions (yes, I've seen XML like 2), how about an option to control the name of the key? ,eg.
    Hash.from_xml(xml, :preserve_attributes => true, :text_value_keyname => :content) [or whatever you want to call it]

    August 21st, 2009 @ 06:22 PM

  10. howardk
    howardk

    Gah! In my comment "Even better, to avoid collisions (yes, I've seen XML like 2)..." the '2' was supposed to be the XML block

    @@@ XML 2

    
    I'm sure everyone's seen XML ranging from pretty and elegant to really ugly and coarse, even down to things like


    @@@XML <ITEM TYPE='PURCHASEORDER'><ID VALUE='123'/></ITEM>

    My point being, any (sane) name you pick for the #text's key in the Hash can collide with attributes in use in the real world, so pick a sane default and give callers the option to override the default key if they have to deal with a collision.

    August 21st, 2009 @ 08:18 PM

  11. howardk
    howardk

    Grrrr. OK, the 'Formatting help' isn't -- it's still mangling my sample XML. Fine. Here's the previous comment, but with curly braces instead of angle brackets. Let's see if he handles that:

    Gah! In my comment "Even better, to avoid collisions (yes, I've seen XML like 2)..." the '2' was supposed to be the XML block

    {BLEH VALUE='1'}2{/BLEH}
    

    I'm sure everyone's seen XML ranging from pretty and elegant to really ugly and coarse, even down to things like

    @@@XML {ITEM TYPE='PURCHASEORDER'}{ID VALUE='123'/}{/ITEM}

    
    My point being, any (sane) name you pick for the #text's key in the Hash can collide with attributes in use in the real world, so pick a sane default and give callers the option to override the default key if they have to deal with a collision.
    

    August 21st, 2009 @ 08:21 PM

  12. laran (at evanscode)
    laran (at evanscode)

    I ran into this today. Bummer! Going to have to work around for sure.

    August 24th, 2009 @ 07:08 AM

  13. sbwoodside
    sbwoodside

    Slightly related is that current implementation can't deal with arrays. See https://rails.lighthouseapp.com/projects/8994/tickets/3133-activere...

    September 2nd, 2009 @ 09:47 PM

  14. Matthew Ford
    Matthew Ford

    I need this too, will try and merge the patch into the current master and report back.

    I like nick's suggestion for the syntax, if preserving XML attributes was an option that needed to be turned on, then I don't see why the same structure that could possibly be generated would be an issue.

    October 28th, 2009 @ 10:10 PM

  15. Jeremy Kemper
    Jeremy Kemper
    • Milestone changed from 2.x to 3.x

    May 4th, 2010 @ 06:48 PM

  16. Rohit Arondekar
    Rohit Arondekar
    • Importance changed from to Low

    Any updates here?

    Besides the milestone update (using bulk edit) this ticket hasn't been updated since October 28th 2009. So is this still an issue or relevant now?

    I'll monitor the comments so in case this is still an issue please do leave a comment, or rebase that patch or make a new one. Else I think it's best to close this ticket as stale.

    October 7th, 2010 @ 05:38 AM

  17. Ryan Bigg
    Ryan Bigg
    • Tag cleared.

    Automatic cleanup of spam.

    October 9th, 2010 @ 09:56 PM

  18. Boris
    Boris

    Indeed, the problem still exists in Rails 3.0.1.

    November 11th, 2010 @ 10:52 AM

  19. Rohit Arondekar
    Rohit Arondekar
    • State changed from new to open
    • Assigned user changed from Pratik to Mikel Lindsaar

    November 12th, 2010 @ 02:39 AM

  20. teiddy
    teiddy

    Instructions: download and untar to /sites/all/modules

    also - Download and install 'Rules' module to /sites/all/modules

    Run /update.php

    Goto Admin>Build>Modules, enable "OA Single Group Login Redirect" module and allow Rules module to be activated when prompted.

    Log-out as admin and log-in as user with 1 group - page redirects as expected.

    Greyside Thank-you!

    Thank you for this information,I like it very much,Would you lik a pair of
    Pachuco Suits
    pack linen clothes
    pack suit into suit
    pant length
    pant length for men

    paul smith mens suits
    Welcome to our store,We have the best service team!

    November 30th, 2010 @ 05:54 AM

  21. bingbing