Question

I am using Builder::XmlMarkup to produce data structures in XML format for a RESTful API server.

Recently, I discovered a bug where the pretty-printing from Builder::XmlMarkup produced an element full of whitespace text instead of an empty element as it should.

For example, this code:

xml.outertag do
  xml.list do
    # Some code which loops through a list
  end
end

is producing:

<outertag>
  <list>
  </list>
</outertag>

When the inner list is an empty list, the element must be empty—i.e. <list/> or <list></list>. However the actual XML is a <list> tag filled with a newline and other whitespace.

So, how can I eliminate Builder pretty-printing altogether? Currently, I am thinking of monkey-patching Builder::XmlMarkup so that initialize ignores the :indent parameters; although I'm considering an after_filter as well.

Was it helpful?

Solution

Calling Builder::XmlMarkup.new without any indent parameter at all shouldn’t give you any whitespace.

xml = Builder::XmlMarkup.new
xml.outertag do
  xml.list do
    # Some code which loops through a list
  end
end

xml # => <outertag><list></list></outertag>

OTHER TIPS

Accepting Matt's answer as it seems the most generally-applicable. However since my situation is an API that outputs XML for every URL, I want something that works across the board.

Here are two solutions for that:

  1. (My current solution) Make a simple plugin that monkey-patches ActionView::TemplateHandlers::Builder to force 0 indentation (as per Matt's answer). Unfortunately, ActionView::TemplateHandlers::Builder is mostly an opaque string of Ruby code that is eval()ed later. Here is my main module, which I include in:

    module MinimalXml
      module Builder
        def self.included(base)
          base.class_eval do
            def compile(template)
              indent = 0
              "_set_controller_content_type(Mime::XML);" +
                "xml = ::Builder::XmlMarkup.new(:indent => #{indent});" +
                "self.output_buffer = xml.target!;" +
                template.source +
                ";xml.target!;"
            end
          end
        end
      end
    end
    
  2. (Probably a future solution.) It occurs to me that a serious XML-based web service needs to process incoming and outgoing XML, to validate it and do whatever else is necessary. That is a good candidate for Rack middleware. So eventually I foresee myself writing a simple Rack middleware layer which will validate the outgoing XML (sounding the alarm if it is invalid, etc.) and then either pretty-print it if in development mode, or compactify it in production.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top