This project is archived and is in readonly mode.

#1598 open
Peter Wagenet

Preserve XML Attributes with Hash#from_xml and ActiveResource

Reported by Peter Wagenet | December 18th, 2008 @ 06:28 PM | in 3.x

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.

Comments and changes to this ticket

  • Pratik

    Pratik March 10th, 2009 @ 11:27 AM

    • 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)
      ...
    
  • Peter Wagenet

    Peter Wagenet March 10th, 2009 @ 03:47 PM

    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.

  • Pratik

    Pratik March 10th, 2009 @ 04:13 PM

    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.

  • Peter Wagenet

    Peter Wagenet March 13th, 2009 @ 12:29 AM

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

  • Peter Wagenet

    Peter Wagenet March 13th, 2009 @ 12:48 AM

    Here's the Rails 2.3 version anyway.

  • Peter Wagenet

    Peter Wagenet March 13th, 2009 @ 01:04 AM

    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.

  • Nick Eskelinen

    Nick Eskelinen April 10th, 2009 @ 09:09 PM

    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"]}
    
    
    
  • Mark Roach

    Mark Roach May 4th, 2009 @ 07:43 PM

    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"} } }

  • howardk

    howardk August 21st, 2009 @ 06:22 PM

    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]

  • howardk

    howardk August 21st, 2009 @ 08:18 PM

    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.

  • howardk

    howardk August 21st, 2009 @ 08:21 PM

    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.
    
  • laran (at evanscode)

    laran (at evanscode) August 24th, 2009 @ 07:08 AM

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

  • sbwoodside

    sbwoodside September 2nd, 2009 @ 09:47 PM

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

  • Matthew Ford

    Matthew Ford October 28th, 2009 @ 10:10 PM

    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.

  • Jeremy Kemper

    Jeremy Kemper May 4th, 2010 @ 06:48 PM

    • Milestone changed from 2.x to 3.x
  • Rohit Arondekar

    Rohit Arondekar October 7th, 2010 @ 05:38 AM

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

  • Ryan Bigg

    Ryan Bigg October 9th, 2010 @ 09:56 PM

    • Tag cleared.

    Automatic cleanup of spam.

  • Boris

    Boris November 11th, 2010 @ 10:52 AM

    Indeed, the problem still exists in Rails 3.0.1.

  • Rohit Arondekar

    Rohit Arondekar November 12th, 2010 @ 02:39 AM

    • State changed from “new” to “open”
    • Assigned user changed from “Pratik” to “Mikel Lindsaar”
  • teiddy

    teiddy November 30th, 2010 @ 05:54 AM

    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!

  • bingbing

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

Attachments

Pages