Processing XML Files with Groovy: Creating

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


Posted by: Jeff Jensen
Posted on: 7/27/2011 at 11:18 PM
Tags: ,
Categories: Database | Java
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Subscribe to this BlogRSS comment feed

Java the Ecosystem - It is Not Just a Language

I have been working with Java and its related technologies since 1997 - the first two years managing a team using the pre-release, 1.0, and 1.1 versions (primarily for the cross-platform benefit to make new front ends for a Unix/C++ system) and the remainder years as a consultant with Intertech. One of my favorite things about Java is its ecosystem, having watched a number of things form (and fade!) during that time.

Recently I had another conversation about its amazing ecosystem, commenting on the various facets of it - such as the JVM, community, products, support, maturity. With the community, it is mind blowing how many free-open-source (FOS) framework and tool products exist and continue to emerge. Yes, many are junk, unfinished, or just interesting (or not!) experiments. However, there are some very useful, successful, proven, well-adopted, and supported ones. We talk about some of them in everyday conversation - Spring, Hibernate, Tomcat, Maven, Ant, Eclipse, et al - almost taking them for granted. The Java ecosystem has enabled many successful new, and expansion of existing, companies.

At the onset, FOS software were few, far-between, and disorganized. Out of chaos emerges organization. Forges, such as SourceForge, emerged to provide some organization and aggregation. Curators such as Apache emerged to help foster higher quality, support, and consistency levels. More recently, Google established Google Project Hosting. Java FOS is very alive and well - thriving in fact. SourceForge reports over 43,000 Java projects.

The community around the FOS products is very large. Most of the products, particularly active ones, will have one or more email lists for help and discussion of the product, and I've seen this support exceed some of the commercial product support! Some products have individuals and/or organizations also offering commercial support contracts for escalated support response and priority for new features and correcting defects. Additionally, organizations using well-adopted FOS products also have an easier time finding people with existing skillsets in them - often learned from and participating in the FOS community.  Just look at some of Intertech's Java training offerings - Eclipse, Spring, Hibernate, iBatis, Struts...!

Another interesting facet of FOS in general is the corporate involvement in creating and supporting it. Besides individuals donating their time and talent, companies such as Google open source their free products that their employees create. Many of us use the Eclipse IDE.  IBM initiated this product and subsequently donated it to the newly established Eclipse Foundation (Eclipse has a very impressive release track record - the processes, tools, and coordination required to is a topic of its own!).  I have had a number of business people ask me about this - fascinated that companies and we developers would do this - and this is yet another topic of its own! :-)

The core enabler of Java's ecosystem is the JVM. It is highly performant, stable, and many languages can run on it. Besides the Java language, better-known ones are Groovy, Scala, JRuby, and Jython. Even ColdFusion has migrated to a JEE application that runs the CF apps. An interesting browse is the VM Languages blog - this site has listed over 300 languages that run on the JVM as of Feb 22, 2010: http://www.is-research.de/info/vmlanguages

Targeting the JVM with your language(s) of choice provides a flexible runtime environment. Most of us also know that there are JVMs for many different platforms, such as Mobile, Windows, UNIX, GNU/Linux, and mainframes. This allows your applications to run on the correct class of hardware for needs such as scaling and operations team skills/support. It also allows developing on the developer workstation of choice, e.g. Mac, Windows, GNU/Linux Desktop, and deploying to a potentially different operations environment, such as Solaris, or a multi-platform runtime environment.

Java's innovating ecosystem expands to other platforms too. A number of its tools and frameworks have been ported to other languages/environments, particularly .Net. For example, Ant -> NAnt, Maven -> NMaven, Spring -> Spring .Net, JUnit -> NUnit, CPPUnit (and many others).

My goal in sharing this is not an in-depth essay of all the merits of Java's ecosystem, but to share some of the recurring conversational points I have had, possibly encouraging you to further explore any of them. Java is very popular and successful, and its ecosystem is simultaneously a contributor and a result of it.


Posted by: Jeff Jensen
Posted on: 6/10/2010 at 1:42 AM
Tags: ,
Categories: Java
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Subscribe to this BlogRSS comment feed

Nexus OSS hosting - fast and easy

An SMTP Test Server

I am working on a new product for a customer, and it will send a number of emails to users.  I also set up sending emails to the development team for runtime diagnostic information for "those situations".

Of course, we need automated integration tests (ITs), aka functional tests, to prove these email features work.  There are a number of OSS SMTP server products to use, and the key thing is to use one that is fast & easy to setup and teardown in a functional test.

This time I chose SubEtha SMTP, a Java library with a simple email test harness named Wiser.  It's fast, easy, and works well in the functional tests.

Not Current

There was one problem though, the latest SubEtha SMTP version in Maven Central repository was really old, and we encountered a couple of problems that were fixed in more recent versions.  We needed the newest version, and since I have been successfully using Maven for over 5 years, and again on this project, this was important to resolve.

Fix It

I could have simply built SubEtha SMTP and deployed it to my customer's local Maven repo, hosted by Nexus (setup and managed by me!).  But that doesn't solve the real problem.  Since SubEtha SMTP was a pretty good product, I wanted to solve the problem permanently and for others.

Create Maven Build

I planned to create one, but found an existing request in SubEtha's issue tracker to deploy a new release to Maven Central and commented in it.  A discussion ensued; the summary is the SubEtha developers didn't care to use Maven or deal with Maven Central repo updates.  However, they were happy to have me do it for them.  So I created a POM for SubEtha SMTP and committed it.  With a simple command, Maven builds and packages the product into a jar, and also creates jars of the source and JavaDoc.  Almost there?

Host It

The next step was to setup something to get the artifacts to Maven Central.  I've done this before on other OSS products I've worked on, and it involved either a manual upload request (no thanks!) or syncing with a self-made repo on a forge, like SourceForge.  This was ok, but took some extra effort on my part and extra time waiting for it to happen.

Enter Nexus

Recently, Sonatype, the creators of Nexus (a Maven repository manager), began offering free Maven repository hosting for OSS products.  They have a page for the Nexus OSS Repository Hosting info.  Anyone can use/view the Nexus OSS repo, browsing the product repos and their artifacts already setup there.  Additionally, if the build artifacts comply with a number of items, the repo owner can request to have the Nexus OSS repo automatically synced with Maven Central.  Hmmm, sounds perfect.

Having setup Nexus for my current and prior customers (I even used and liked its predecessor, Proximity!), I know what a great product it is.  With the need to get SubEtha SMTP to Maven Central repo, this seemed like the best option - using it as the public repo and auto-syncs to Central.

Couldn't Be Easier

Setting it up was very easy - I just read and followed the information on the Nexus OSS Repo Hosting page.  I created the JIRA ticket requesting the Nexus repo on a Saturday, and Monday morning it was created.  That Monday night, I deployed to the new repo, and then requested the auto-sync to Central.  Approximately 5 hours later, the auto-sync was approved and occurred!  It can't get faster and easier than that! 

Done - D. U. N.

As my wonderful grandfather used to say, "Done, D  U  N"; I have very fond memories of him teasing me with that errant spelling when I was wayyyy too young to understand!  :-)

I reported the successful deploy to Maven Central on the SubEtha SMTP issue, closed it as fixed, and received a couple of thank you's :-).

Thank you Sonatype for creating Nexus and offering this service!  It is fantastic.  I'll move other projects' artifacts to there as well.

P.S.

Interestingly, with the many problems on java.net, especially with the (lack of) support for Maven deployed artifacts there, Sonatype is orchestrating a great effort to help java.net projects migrate their Maven repo hosting to Nexus OSS.  For more information on that, read the Sonatype blog on Java.net Maven Repository Rescue Mission on March 5th.


Posted by: Jeff Jensen
Posted on: 3/1/2010 at 11:34 PM
Tags: , ,
Categories: Java
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Subscribe to this BlogRSS comment feed
Contact Us 651-994-8558 1-800-866-9884
Home | Training | Curriculum | Course Finder | Schedule | Enroll | Twin Cities Java User Group | Consulting | Foundation | Jobs | About Us | Our Story | Press Room | Instructors | President | Map & Directions | Sitemap

Java Training | JSF / Struts / Spring / Hibernate Training | Java Power Tools Training | .NET 4.0 & Visual Studio 2010 Training | .NET 3.5 and Visual Studio 2008 Training | .NET 2.0 and Visual Studio 2003 Training | Prism / MVVM / MEF Training | Microsoft Web Development Training | Cloud Computing Training | Ajax / Web Services / XML Training | Groovy and Grails Training | SQL Server 2008 Training | SQL Server 2005 Training | Mobile Development Training | SharePoint 2010 Training | SharePoint 2007 Training | Agile, Process, Analysis & Design Training | Arch/Design Patterns Training | Microsoft Official Curriculum Training | Web Development Training | Ruby Training | Rational Application Developer (RAD) Training | WebSphere Application Server Training | WebSphere Portal Training | WebLogic Training | Boot Camp Training | Project Management Training | C++ Training | Metro / WinRT / Windows 8 Development Training | Retired

Intertech delivers training on-site and virtually serving cities including Phoenix, AZ | San Francisco, CA | Los Angeles, CA | San Diego, CA | San Jose, CA | Washington, DC | Chicago, IL | Orlando, FL | Boston, MA | Duluth, MN | Minneapolis St. Paul, MN | Rochester, MN | Raleigh-Durham, NC | New York, NY | Philadelphia, PA | Austin, TX | Dallas, TX | Houston, TX | Seattle, WA.