# ActiveRecord .from\_xml upgrade

**URL:** <https://www.ruby-forum.com/t/activerecord-from-xml-upgrade/162286>\
**Category:** Ruby\
**Created:** [March 8, 2009, 2:55am UTC](https://www.ruby-forum.com/t/activerecord-from-xml-upgrade/162286 "2009-03-08T02:55:54Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Phlip](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/ed8c4c/32.png) [@Phlip](https://www.ruby-forum.com/u/Phlip)\
**Post date:** [March 8, 2009, 2:55am UTC](https://www.ruby-forum.com/t/activerecord-from-xml-upgrade/162286/1 "2009-03-08T02:55:54Z")

</div>

Rubies:

The gist of this tiny code snip…

```
http://gist.github.com/75525

```

…is a light but flexible DSL that converts XML - typically output by  
to\_xml()

- into an ActiveRecord object model.

==create or update records==

Here’s the simplest example:

```
xml ='<photos>
        <photo>
          <id>323285</id>
        </photo>
        <photo>
          <id>323310</id>
        </photo>
...
      </photos>'

doc = Nokogiri::XML(xml)
photos = doc.from_xml(Photo, :id)

```

(Note that from\_xml{} is a member of a Node, not of your Model.)

That code created new Photo records with matching IDs. If any record  
were  
already there, the code would update it instead.

==rename fields and pass in data==

Here’s the next more complicated example:

```
authors = node.from_xml(Author, [:id, :remote_id], :name)

```

The code reads an tag, then finds or creates an author with a  
matching author.remote\_id. Then the code updates the author.name, and  
return an array of authors.

==associations==

from\_xml takes an optional &block, and yields into this the record under  
construction, before its .save! call. Use this block to seek nested  
data, and  
plug them into their parent record:

```
doc.from_xml Post, :id, :title, :body do |post, node, *|
  post.tags = node.from_xml(Tag, :id, :name)
  post.author = *node.from_xml(Author, :id, :name)
  post.save!
end

```

from\_xml{} will call that block each time it finds a (top-level)   
record,  
and each nested node.from\_xml{} call will only find records inside that  
main record.

(Note, also, that records, for example, should be shared between  
many  
 records, and your XML will probably just duplicate them many  
times, but  
from\_xml(Tag) knows to fold them all back together again…)

The splat operator \* threw away three more arguments - they were the  
string  
values of the id, title, and body fields.

==raw XML==

To scan your XML with very similar abilities, but without using a Model  
with the  
correct name to match your XPath, write the XPath directly into the  
lower-level  
convert{} method:

```
  node.convert 'tags/tag', :id, :name do |n, id, name|
    tag = Tag.find_or_initialize_by_id(id)
    tag.update_attribute :name, name
     # or tag.attributes = n.data
    post.tags << tag
  end

```

That block shows form\_tag{} “unrolled” into its low-level behavior.  
convert{}  
takes an XPath query, relative to the current node, and a list of fields  
(and  
their renamers) to extract. Then it yields the detected node (don’t call  
it  
“node”!) into its |goal posts|, with the string value of each detected  
field.

Your block could have done something more complex, but this one merely  
simulated  
form\_tag{} by reconstituting and updating a Tag record, then inserted it  
into  
some outer post object.

One more detail - the renamed fields, and their string values, are also  
available as a hash. To avoid even more extra arguments into our goal  
posts, the  
committee stashed them into the passed node, as an attribute called  
“node.data”.  
So the little comment shows how to update all your Model attributes at  
once.

==what about to\_xml?==

One ActiveRecord FAQ goes, “Why can’t from\_xml take the same arguments  
as  
to\_xml?” The reason is creation is harder than just reading an existing  
object  
model. While a future version of from\_xml{} could indeed learn to follow  
model  
associations, and could take a big blob of nested hashes, like most  
other  
ActiveRecord methods, the committee does not foresee this DSL exactly  
matching  
the input to to\_xml(). That is a goal for further research on both  
sides!
