On Fri, 2011-09-02 at 11:02 -0400, Tong Li wrote:
> When implementing RESTful APIs, often you have to support both XML and JSON
> (sometimes also ATOM Feed), Ruby has nice functions to convert between XML
> and JSON. In DeltaCloud, very often see code like this.

We used to convert directly from model objects to XML/JSON; that's kinda
dangerous, because any change in a model object would lead to a change
in the output XML, i.e. it was too easy to break the API with an
innocuous-looking code change.

That's why we switched to explicit templates, so that output changes
require a more conscious developer action.

Frankly, I hadn't thought about the route of generating JSON from XML;
it would require that you generate the XML, parse it into a DOM, and
then transform it to JSON. We haven't established fixed rules for the
XML -> JSON conversion, but it seems there's a natural mapping that
converts the DOM to a Hash:

        dom_to_json(<elt attrs>body</elt>) =
                { "elt" => attrs.inject({}) { |h, k,v| h[k] = v 
}.merge(convert(body)) }
                
        dom_to_json(<elt1 ...>...</elt1> rest) =
                h = convert(rest)
                e = convert(<elt1 ...>...</elt1>)
                if h["elt1"]
                        if h["elt1"].is_a?(Array)
                                h["elt1"] << e["elt1"]
                        else
                                h["elt1"] = [ h["elt1"], e["elt1"] ]
                        end
                end

That of course only works if there's no conflict between attribute names
and the names of child elements; but that should be the case for the
Deltacloud XML.

With that, your typical respond_to block would then look like

        respond_to do |format|
                format.xml { haml :"index" }
                format.html { haml :"index" }
                format.json { xml_to_json(xml_parse(haml :"index", :format => 
:xml)) }
        end

Of course, all that repetitive json mumbo jumbo should be hidden in a
helper.

BTW, be careful with Rails' Hash.from_xml - it drops attributes, e.g.:

        >> Hash.from_xml("<elt attr='1'>body</elt>")
        => {"elt"=>"body"}

>       This is to ensure that the json format contains absolutely same
> information as in xml format and only provide one xml haml file, the
> problem is that now I do not know how to change the request so that the
> template searches for index.xml.haml rather than index.json.haml when
> request for json. I think there should be a way to do this, but within DC
> rabbit, I do not know if there is an easy way.

The 'haml' function actually comes from Sinatra[1]; you can specify a
format explicitly with 'haml :template, :format => :xml' etc.

>  of course, hash data can be
> directly converted to json, but sometimes, the xml haml may take in data
> from more than one objects, it will be a lot easier to simply direct the
> request to xml and get the xml object then convert it to json or even feed,
> Any pointer will be appreciated.

Yes, I really like this idea; do you think you could write a patch for
that ?

David

[1] http://www.sinatrarb.com/intro#Haml%20Templates


Reply via email to