Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extra <to_s/> when using builder to generate XML

I'm trying to generate KML using Builder. I know their are some options out there to help with this but I will be doing some 2.2 specific things that aren't supported by the KML gems I've looked at and would generally like to be able to accomplish this leveraging just an XML framework.

I get a tag at the end of the file when rendering my kml/xml. I strongly suspect I'm missing something basic with setting up my Builder object or with how I'm rendering the output it. Here's a simple example that demonstrates the issue:

def kml2dot2
  @site = Site.find(params[:id])
  xml = Builder::XmlMarkup.new(:indent => 2)
  xml.instruct!
  xml.kml("xmlns" => "http://www.opengis.net/kml/2.2") {
    xml.Placemark do
      xml.name @site.mapNameFull
      xml.Point do
        xml.coordinates @site.lat.to_s + "," + @site.lng.to_s + ",0"
      end
    end
  }
  render :text => xml, :type=>"text/kml"

end 

Produces:

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <Placemark>
    <name>Seattle City Hall</name>
    <Point>
      <coordinates>47.6040746,-122.33005,0</coordinates>
    </Point>
  </Placemark>
</kml>
<to_s/>

I'm trying to understand how to avoid the <to_s/> being included and what I'm doing wrong with Builder. Thanks for any insight.

like image 673
Nick Avatar asked Feb 10 '11 19:02

Nick


2 Answers

My short original answer

To get the actual string contents of the Builder you need to call the method target!

xml = Builder.new
# do your stuff...
xml.target! #returns the string
#where as calling most other methods (like to_s) to the builder object will just
#generate an element tag by that method name.


And then a bit more verbose explanation of what and why is going on in the OPs case

When you pass the xml builder object to the render method Rails will automatically call the to_s method for it. Usually this means that you do not need to worry about the type of data that you pass for the renderer, since it will be converted to a String anyway. Very convenient! However, with the Builder object you need to do the conversion your self since the builder assumes that any message sent to it is a request to add a new element by the name. So calling xml.to_s behaves just the same as calling xml.kml, adds a new element. In this case your not calling to_s your self so it is not so apparent and is easy to miss. The simple fix to this is to call render like this:

render :text => xml.target!, :type=>"text/kml"
like image 124
Timo Avatar answered Nov 19 '22 23:11

Timo


You don't need to initialize an XML builder object. Just use the integrated builder template handler.

  1. Call the template kml2dot2.xml.builder
  2. Write the code directly in the view

Example

def kml2dot2
  @site = Site.find(params[:id])
end

# kml2dot2.xml.builder
xml.kml("xmlns" => "http:// www.opengis.net/kml/2.2") do
  xml.Placemark do
    xml.name @site.mapNameFull
    xml.Point do
       xml.coordinates "#{@site.lat},#{@site.lng},0"
    end
  end
end
like image 1
Simone Carletti Avatar answered Nov 19 '22 23:11

Simone Carletti