651.288.7000 info@intertech.com

Intro

Do you write code that reads and/or writes XML files or in-memory structures?  XML files are a useful data exchange for programs, but are not usually fun to hand-edit code for in most languages.

If you haven’t seen how easily Groovy handles XML data, read on…  It can simplify the coding such that you will probably want to use Groovy for this next time!  It can be particularly useful with test data.

This post covers an intro how to on creating XML with Groovy.  I plan to cover an intro how to on parsing XML with Groovy in the future.

For the examples, while much Groovy convention is to chain method calls, I deliberately separated object definitions and method calls into more simple statements to help understand the separate pieces as an introduction.  Additionally, the examples were written with Groovy 1.8.0.

Creating XML

Groovy primarily has the MarkupBuilder and StreamingMarkupBuilder classes that do the XML creation dirty work (along with their helper classes).  These examples only focus on the StreamingMarkupBuilder as it also has namespace support and handles large documents with less memory usage.  However, using the others is very similar.

The “writeFile” Method for All Examples

Before jumping into the XML creation examples, we need to cover a common method used by each example: “writeFile”.  I extracted the common code from each example to it to simplify the example, and show the near-template code for setup and use.

   1: def writeFile(fileName, closure) {
   2:     def xmlFile = new File(fileName)
   3:     def writer = xmlFile.newWriter()
   4:  
   5:     def builder = new StreamingMarkupBuilder()
   6:     def Writable writable = builder.bind closure
   7:     writable.writeTo(writer)
   8: }

The writeFile method requires two parameters (line 1):

  • The first parameter is the full path of the file name to create.
  • The second parameter is the closure passed to the StreamingMarkupBuilder’s bind method.

Using the StreamingMarkupBuilder class:

  • The bind method requires a closure and returns a Writable instance (line 6).  The closure contains the code for generating the XML.  The bind method will invoke the closure to generate the XML.
  • Use the Writable (line 7) to output the XML from the closure to the desired destination, whether a file or a String (review the doc or use autocomplete in your favorite IDE to learn the other uses of Writable).

The following examples will explain creating the closure for writing. the XML.  They start with a simple example and each adds one new “feature” to help keep this “how to” clearer.  The last example shows dynamically generating the XML from a map of data versus statically defined in the closure.

Example 1: Simple

Example 1 shows creating a simple XML file from static elements defined in the closure.

   1: def createFromStatic_Simple() {
   2:     def xmlClosure = {
   3:         docroot {
   4:             element(1)
   5:             element('b')
   6:             element('c' + 'c')
   7:             element(2 + 2)
   8:         }
   9:     }
  10:  
  11:     writeFile('/createFromStaticSimple.xml', xmlClosure)
  12: }

 

Example 1 creates the following XMLfile (note: it’s created all on one line without formatting; it is formatted it here for readability):

   1: <docroot>
   2:   <element>1</element>
   3:   <element>b</element>
   4:   <element>cc</element>
   5:   <element>4</element>
   6: </docroot>

Comparing the closure defined starting on line 2 of the code example with the created XML file, notice the direct use of the closure structure names and evaluated expressions in the XML file.

  1. Literal names in the closure structure become the element names in the XML file.
    • “docroot” and “element” become element names.
  2. Expressions inside the parenthesis are evaluated and used as the element’s value.
    • ‘c’ + ‘c’ on code line 6 becomes “cc” on XML line 4.
    • “2 + 2” on code line 7 becomes 4 on XML line 5.
  3. Elements inside the curly braces become the next nested level inside the named element (they are each closures – anonymous closures as they are not bound to variables – and are evaluated at runtime).  The “element”s on code lines 4-6 become <element> entries on XML lines 2-5.

Note: It’s important to place the subsequent element definitions on their own line, otherwise, Groovy will dynamically interpret it differently, usually as a map!

Example 2: Nested Elements

Example 2 also shows creating an XML file from static elements defined in the closure, and adds a second nested level of closure elements named “inner”.

   1: def createFromStatic_Nested() {
   2:     def xmlClosure = {
   3:         docroot {
   4:             element {
   5:                 inner(1)
   6:             }
   7:             element {
   8:                 inner('b')
   9:             }
  10:             element {
  11:                 inner('c' + 'c')
  12:             }
  13:             element {
  14:                 inner(2 + 2)
  15:             }
  16:         }
  17:     }
  18:  
  19:     writeFile('/createFromStaticNested.xml', xmlClosure)
  20: }
 

Example 2 creates the following XMLfile (note: it’s created all on one line without formatting; it is formatted it here for readability):

   1: <docroot>
   2:   <element>
   3:     <inner>1</inner>
   4:   </element>
   5:   <element>
   6:     <inner>b</inner>
   7:   </element>
   8:   <element>
   9:     <inner>cc</inner>
  10:   </element>
  11:   <element>
  12:     <inner>4</inner>
  13:   </element>
  14: </docroot>

Once again, notice the direct use of the element names in the XML file, this time with the addition of another nested element level named “inner”.  The same value expressions are moved from the “element” element to the new “inner” element, still within parenthesis.

As the code example shows, to add a nested element, just create a nested closure.

Example 3: Attributes

Example 3 also shows creating an XML file from static elements defined in the closure with nested elements, and adds attributes.

   1: def createFromStatic_NestedAndAttributes() {
   2:     def xmlClosure = {
   3:         docroot {
   4:             element(attr1:1, attr2:2) {
   5:                 inner(attr3:1, 1)
   6:             }
   7:             element(attr1:4, attr2:5) {
   8:                 inner(attr3:'b', 'b')
   9:             }
  10:             element(attr1:7, attr2:8) {
  11:                 inner(attr3:'c' + 'c', 'c' + 'c')
  12:             }
  13:             element(attr1:10, attr2:11) {
  14:                 inner(attr3:2 + 2, 2 + 2)
  15:             }
  16:         }
  17:     }
  18:  
  19:     writeFile('/createFromStaticNestedAndAttr.xml', xmlClosure)
  20: }

Example 3 creates the following XMLfile (note: it’s created all on one line without formatting; it is formatted it here for readability):

   1: <docroot>
   2:   <element attr1='1' attr2='2'>
   3:     <inner attr3='1'>1</inner>
   4:   </element>
   5:   <element attr1='4' attr2='5'>
   6:     <inner attr3='b'>b</inner>
   7:   </element>
   8:   <element attr1='7' attr2='8'>
   9:     <inner attr3='cc'>cc</inner>
  10:   </element>
  11:   <element attr1='10' attr2='11'>
  12:     <inner attr3='4'>4</inner>
  13:   </element>
  14: </docroot>

The only difference between examples 2 and 3 is 3 adds element attributes.  To add attributes to an element:

  1. Specify the attributes inside parenthesis, comma separated, followed by an optional value expression as seen in examples 1 and 2.
  2. Specify an attribute with its name, a colon, and its value-expression.

Example 4: Dynamic Creation

Example 4 shows creating an XML file from a map of data using for-each loops in the closure.

   1: def createFromDynamic() {
   2:     def mfgs = [
   3:             'Boeing':['747', '737', '727'],
   4:             'Airbus':['A330', 'A350', 'A380'],
   5:             'Lockheed':['L100', 'L1011', 'C130'],
   6:             'Ilyushin':['IL-62', 'IL-76', 'IL-86'],
   7:             'Fokker':['F28', '50/60', '70/100']]
   8:  
   9:     def closure = {
  10:         manufacturers {
  11:             mfgs.each { key, value ->
  12:                 manufacturer(name:key) { value.each { model (it) }}
  13:             }
  14:         }
  15:     }
  16:  
  17:     writeFile('/createFromDynamic.xml', closure)
  18: }

Example 4 creates the following XMLfile (note: it’s created all on one line without formatting; it is formatted it here for readability):

   1: <manufacturers>
   2:   <manufacturer name='Boeing'>
   3:     <model>747</model>
   4:     <model>737</model>
   5:     <model>727</model>
   6:   </manufacturer>
   7:   <manufacturer name='Airbus'>
   8:     <model>A330</model>
   9:     <model>A350</model>
  10:     <model>A380</model>
  11:   </manufacturer>
  12:   <manufacturer name='Lockheed'>
  13:     <model>L100</model>
  14:     <model>L1011</model>
  15:     <model>C130</model>
  16:   </manufacturer>
  17:   <manufacturer name='Ilyushin'>
  18:     <model>IL-62</model>
  19:     <model>IL-76</model>
  20:     <model>IL-86</model>
  21:   </manufacturer>
  22:   <manufacturer name='Fokker'>
  23:     <model>F28</model>
  24:     <model>50/60</model>
  25:     <model>70/100</model>
  26:   </manufacturer>
  27: </manufacturers>

The XML file structure is still defined in the closure (the element and attribute names, and their relationships), but the data is now pulled from a map.  This example shows a typical use – the map (or a list) could have been retrieved from a database or other source.

Conclusion

The examples begin to show easy ways to programmatically create XML files with Groovy.  While these simple examples should get you started, the potential processing is only limited by the closure logic.  Additionally, StreamingMarkupBuilder (and other builders) has some advanced features often accessed through “mkp”, which is a special namespace used to access helper markup methods.

Resources

Pin It on Pinterest

Share This

Like What You've Read?

Subscribe to the Blog.

Every Friday we send that week's content from our Developers via email. Try it out!

Some ad blockers can block the form below.

You have Successfully Subscribed!