Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add/insert elements into Groovy MarkupBuilder object after initial creation

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?

like image 542
Eric Levine Avatar asked Dec 29 '22 14:12

Eric Levine


2 Answers

The cookbook example Using MarkupBuilder for Agile XML Creation shows how to create something close to what I need.

For example:

import groovy.xml.MarkupBuilder

def writer = new StringWriter()
def builder = new MarkupBuilder(writer)  

def subitem (builder){
    builder.subitem(type: "paperback")
}


def items = builder.items{
    item(name: "book")
    subitem(builder)
}

println writer.toString()​
like image 138
Eric Levine Avatar answered Jan 01 '23 04:01

Eric Levine


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.

like image 27
Michael Easter Avatar answered Jan 01 '23 03:01

Michael Easter