Monday, February 9, 2015

Writing Groovy's groovy.util.Node (XmlParser) Content as XML

Groovy's XmlParser makes it easy to parse an XML file, XML input stream, or XML string using one its overloaded parse methods (or parseText in the case of the String). The XML content parsed with any of these methods is made available as a groovy.util.Node instance. This blog post describes how to make the return trip and write the content of the Node back to XML in alternative formats such as a File or a String.

Groovy's MarkupBuilder provides a convenient approach for generating XML from Groovy code. For example, I demonstrated writing XML based on SQL query results in the post GroovySql and MarkupBuilder: SQL-to-XML. However, when one wishes to write/serialize XML from a Groovy Node, an easy and appropriate approach is to use XmlNodePrinter as demonstrated in Updating XML with XmlParser.

The next code listing, parseAndPrintXml.groovy demonstrates use of XmlParser to parse XML from a provided file and use of XmlNodePrinter to write that Node parsed from the file to standard output as XML.

parseAndPrintXml.groovy : Writing XML to Standard Output
#!/usr/bin/env groovy

// parseAndPrintXml.groovy
//
// Uses Groovy's XmlParser to parse provided XML file and uses Groovy's
// XmlNodePrinter to print the contents of the Node parsed from the XML with
// XmlParser to standard output.

if (args.length < 1)
{
   println "USAGE: groovy parseAndPrintXml.groovy <XMLFile>"
   System.exit(-1)
}

XmlParser xmlParser = new XmlParser()
Node xml = xmlParser.parse(new File(args[0]))
XmlNodePrinter nodePrinter = new XmlNodePrinter(preserveWhitespace:true)
nodePrinter.print(xml)

Putting aside the comments and code for checking command line arguments, there are really 4 lines (lines 15-18) in the above code listing of significance to this discussion. These four lines demonstrate instantiating an XmlParser (line 15), using the instance of XmlParser to "parse" a File instance based on a provided argument file name (line 16), instantiating an XmlNodePrinter (line 17), and using that XmlNodePrinter instance to "print" the parsed XML to standard output (line 18).

Although writing XML to standard output can be useful for a user to review or to redirect output to another script or tool, there are times when it is more useful to have access to the parsed XML as a String. The next code listing is just a bit more involved than the last one and demonstrates use of XmlNodePrinter to write the parsed XML contained in an Node instance as a Java String.

parseXmlToString.groovy : Writing XML to Java String
#!/usr/bin/env groovy

// parseXmlToString.groovy
//
// Uses Groovy's XmlParser to parse provided XML file and uses Groovy's
// XmlNodePrinter to write the contents of the Node parsed from the XML with
// XmlParser into a Java String.

if (args.length < 1)
{
   println "USAGE: groovy parseXmlToString.groovy <XMLFile>"
   System.exit(-1)
}

XmlParser xmlParser = new XmlParser()
Node xml = xmlParser.parse(new File(args[0]))
StringWriter stringWriter = new StringWriter()
XmlNodePrinter nodePrinter = new XmlNodePrinter(new PrintWriter(stringWriter))
nodePrinter.setPreserveWhitespace(true)
nodePrinter.print(xml)
String xmlString = stringWriter.toString()
println "XML as String:\n${xmlString}"

As the just-shown code listing demonstrates, one can instantiate an instance of XmlNodePrinter that writes to a PrintWriter that was instantiated with a StringWriter. This StringWriter ultimately makes the XML available as a Java String.

Writing XML from a groovy.util.Node to a File is very similar to writing it to a String with a FileWriter used instead of a StringWriter. This is demonstrated in the next code listing.

parseAndSaveXml.groovy : Write XML to File
#!/usr/bin/env groovy

// parseAndSaveXml.groovy
//
// Uses Groovy's XmlParser to parse provided XML file and uses Groovy's
// XmlNodePrinter to write the contents of the Node parsed from the XML with
// XmlParser to file with provided name.

if (args.length < 2)
{
   println "USAGE: groovy parseAndSaveXml.groovy <sourceXMLFile> <targetXMLFile>"
   System.exit(-1)
}

XmlParser xmlParser = new XmlParser()
Node xml = xmlParser.parse(new File(args[0]))
FileWriter fileWriter = new FileWriter(args[1])
XmlNodePrinter nodePrinter = new XmlNodePrinter(new PrintWriter(fileWriter))
nodePrinter.setPreserveWhitespace(true)
nodePrinter.print(xml)

I don't show it in this post, but the value of being able to write a Node back out as XML often comes after modifying that Node instance. Updating XML with XmlParser demonstrates the type of functionality that can be performed on a Node before serializing the modified instance back out.

No comments: