views:

386

answers:

1

I understand how to create XML in Groovy using MarkupBuilder. How do you add/insert elements into a MarkupBuilder object after the initial creation? For example, start with:

def builder = new MarkupBuilder(writer)
  def items = builder.items{
        item(name: "book")
  }

Which would produce:

<items> 
   <item name="book/> 
 </items>

I am trying to create an extensible base XML message, using a core class to wrap the builder and inheritance to add specific tags. Building on the above example, here is my base class:

Class ItemBuilder{
   def name;
   def builder = new MarkupBuilder(writer)
   public Object getXML(){
     def items = builder.items{
            item(name: this.name)
      }
     return items;
   }
}

Here is an example extended message builder:

Class SubItemBuilder extends ItemBuilder{
       def type;

       public Object getXML(){
         def items = super.getXML();
         //do something here to add a subitem child tag....
         return items;
       }
    }

If I was working with JSON in JavaScript, I would do something like:

items.item.subitem = "foo"

I ultimately want SubItemBuilder.getXML to generate:

<items> 
     <item name="book>
       <subitem type="paperback"/>
      </item>
 </items>

Is there any easy way to achieve this? Seems like one option is to subclass MarkupBuilder and add public methods to insert child nodes. Is there a better approach to achieve the result I'm looking for?

+1  A: 

I don't know the answer but I suggest that you restructure the attempt.

Usually, either (a) all output is known at the time of creating the builder or (b) the program operates within the scope of the builder's closure. I haven't seen a builder used as a document per se.

There is one other option: the StreamingMarkupBuilder. This class doesn't construct the doc until it is handed off to a writer (i.e. asynchronous). It also makes it easier to construct the doc from multiple sources, as shown here:

import groovy.xml.*

def builder = new StreamingMarkupBuilder()

def item1 = { item(name:'book') }
def item2 = { item(name:'mag') }

def doc = { 
    "items" {
        out << item1
        out << item2
    }
}

println builder.bind(doc)

>>> <items><item name='book'/><item name='mag'/></items>

I don't think this will solve your problem though. Can you rephrase the question with a bit more scope? That way, we might be able to see if there is a philosophical issue with the larger picture.

Michael Easter
I edited my original question and added more details. Your answer seems like its going in the right direction, but still not quite what I need. Thanks for the help!
elevine