Ruby on Rails
HowtoGenerateXml

This is how I’m doing it, anyway. I’ll leave it up to the experts to explain how to do it properly.

I have a controller called (say), ‘hats_controller.rb’, which contains the following:

class HatsController < ApplicationController
  ...
  def hatsml
    @hats = Hats.find(:all)
    render :layout => false
  end
  . . .
end 

Note that @hats is populated before anything else. If you had the Paginator helper running, here’s where you override it. I also have a RXML template in ‘/views/hats/hatsml.rxml’. (In Rails 2.0+ this would be ‘/views/hats/hatsml.xml.builder’):

xml.instruct! :xml, :version=>"1.0"
xml.instruct! :rss, :version=>"2.0"
xml.channel{
  for hat in @hats
    xml.hat do
      xml.title(hat.name)
      xml.description(hat.description)
    end
  end
}

And then I can see the output at ‘/hats/hatsml’. Nice.

RXML files use Builder (http://builder.rubyforge.org/).

Also note, if you’ve scaffolded the CRUD for your object, you’ll want to remove the lines where it formats the XML automatically:

respond_to do |format|
  format.html # index.html.erb
  format.xml  { render :xml => @hats }
end

Change that to so something like:

respond_to do |format|
format.html # index.html.erb
format.xml # index.xml.builder
end

or just remove the whole respond_to block (in Rails 2.0 you don’t need it)

See the Railscast on generating RSS for a good intro to all of this.

Contenttype

It is generally a good idea to serve your XML responses with a proper contenttype defined in the headers. See HowtoChangeTheContentType.

Parent/Child Data

For my application, I return a list of foods, each of which as one or more portions. Building on the hats example, I ended up with this:

MODELS:
class Food < ActiveRecord::Base
  has_many :portions
end

class Portion < ActiveRecord::Base
  belongs_to :food
end

CONTROLLER:
class InvoiceController < ApplicationController
  def list_foods
    @foods = Food.find(:all, :include => [:portions])
  end
end

VIEW:
xml.instruct! :xml, :version=>"1.0" 
xml.foods{
	for food in @foods
 		xml.food do
			xml.id(food.id)
			xml.calories(food.calories)
			xml.description(food.description)
			xml.short_description(food.short_description)
		
			for portion in food.portions
				xml.portion do
					xml.grams(portion.grams)
					xml.amount(portion.amount)
					xml.serving(portion.serving)
				end
			end	
		end
	end
}

Which creates XML like this:

<foods>

 <food>
  <id>9193</id>
  <calories>115</calories>
  <description>Olives, ripe, canned (small-extra large)</description>
  <short_description>Olives,Ripe,Cnd (Small-Extra Lrg)</short_description>

  <portion>
   <grams>8.4</grams>
   <amount>1.0</amount>
   <serving>tbsp</serving>
  </portion>

  <portion>
   <grams>4.4</grams>
   <amount>1.0</amount>
   <serving>large</serving>
  </portion>

  <portion>
   <grams>3.2</grams>
   <amount>1.0</amount>
   <serving>small</serving>
  </portion>
 </food>

 <food>
  <id>9194</id>
  <calories>81</calories>
  <description>Olives, ripe, canned (jumbo-super colossal)</description>
  <short_description>Olives,Ripe,Cnd (Jumbo-Super Colossal)</short_description>

  <portion>
   <grams>8.3</grams>
   <amount>1.0</amount>
   <serving>jumbo</serving>
  </portion>

  <portion>
   <grams>15.0</grams>
   <amount>1.0</amount>
   <serving>super colossal</serving>
  </portion>
 </food>
</foods>

Remember to append the dump Ruby string method on any text that might have special characters in it, as they can cause some browsers cough IE to complain in stereotypically opaque ways.

XML attributes can be added like this:

xml.link    :rel=>'self', 
    :href=>url_for(:only_path=>false, :action=>'posts', :path=>['index.atom'])

For a tag with both attributes and content:

xml.a    "the link text here", :href => "/your/url" do