If you've been following along, you already know how to parse XML documents using both SAX and the DOM with the Java-based Xerces XML parser. But why stop there? In this article, take your Java/XML skills to the next level by converting your XML into other formats with the very powerful Xalan XSLT engine
In case you're wondering, first come the classes for JAXP, followed by the classes
for exception handling and file I/O. Now, I bet you're wondering, what's JAXP? According to the Unofficial JAXP FAQ, available at http://xml.apache.org/~edwingo/jaxp-faq.html, the Java API for XML Processing (JAXP) "enables applications to parse and transform XML documents using an API that is independent of a particular XML processor implementation". Or, to put it very simply, JAXP provides an abstraction layer, or standard API, that allows for code reuse across different XSLT processors. You might be wondering how JAXP, as an abstraction layer, knows which class to use during the transformation process. This information is available via the javax.xml.transform.TransformerFactory property. In case this didn't make any sense to you, don't worry about it; if it did, and you want to know more, take a look at http://xml.apache.org/xalan-j/apidocs/javax/xml/transform/TransformerFactory.html Next, I've instantiated some variables to hold the names of the various files I'll be using in the application.
// store the names of the files
public static String xmlFile, xslFile,
resultFile = "";
And now for the constructor:
// constructor
public addressBookConverter(String xmlFile, String xslFile,
String
resultFile) {
try {
// create an instance of the TransformerFactory
// this allows the developer to use an API that is independent
of
// a particular XML processor implementation.
TransformerFactory
tFactory = TransformerFactory.newInstance();
// create a transformer
which takes the name of the stylesheet
// as an input parameter
Transformer transformer = tFactory.newTransformer(new
StreamSource(xslFile));
// transform the given XML file
transformer.transform(new
StreamSource(xmlFile), new
StreamResult(resultFile));
System.out.println("Done!");
} catch (TransformerException e) {
System.err.println("The
following error occured: " + e);
}
}
The first step is to create an instance of the TransformerFactory class. This
can be used to create a Transformer object, which reads the XSLT stylesheet and converts the templates within it into a Templates object. This Templates object is a dynamic representation of the instructions present in the XSLT file - you won't see any reference to it in the code above, because it all happens under the hood, but trust me, it exists.
Once the stylesheet is processed, a new Transformer object is generated. This Transformer object does the hard work of applying the templates within the Templates object to the XML data to produce a new result tree, via its transform() method. The result tree is stored in the specified output file.
Finally, the main() method sets the ball in motion:
// everything starts here
public static void main (String[] args) {
if(args.length != 3) {
System.err.println("Please specify three
parameters:n1.
The name and path to the XML file.n2. The name and path to the
XSL
file.n3. The name of the output file.");
return;
}
// assign the parameters passed as input parameters to
// the variables defined above
xmlFile = args[0];
xslFile = args[1];
resultFile = args[2];
addressBookConverter myFirstExample = new
addressBookConverter
(xmlFile, xslFile, resultFile);
}
This method first checks to see if the correct number of arguments was passed.
If so, it invokes the constructor to create an instance of the addressBookConverter class; if not, it displays an appropriate error message.{mospagebreak title=Six Degrees Of Separation} Let's move on to something a little more complicated. Let's suppose that I wanted to convert my address book from XML into a delimiter-separated ASCII file format, for easy import into another application. With XSLT and Xalan, the process is a snap.
Now, in order to convert this XML document into a delimiter-separated file, I
need an XSLT stylesheet like this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<!-
Set the "|" symbol as the default delimiter à
<xsl:param name="delimiter"
select="normalize-space('|')"/>
<xsl:template match="/addressbook">
<xsl:for-each select="item">
<xsl:value-of select="normalize-space(name)"/>
<xsl:value-of select="$delimiter"/>
<xsl:value-of select="normalize-space(address)"/>
<xsl:value-of select="$delimiter"/>
<xsl:value-of select="normalize-space(tel)"/>
<xsl:value-of select="$delimiter"/>
<xsl:value-of select="normalize-space(email)"/>
<xsl:value-of select="$delimiter"/>
<xsl:value-of select="normalize-space(url)"/>
<!- hexadecimal value for the new-line character à
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
And here's the Java code to tie it all together:
// imported java classes
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
import
java.io.*;
public class xml2csv {
// store the names of the files
public
static String xmlFile, xslFile, resultFile,delimiterValue =
"";
// parameters
for the getAssociatedStylesheet() method of the
TransformerFactory class
//
Set them to null as they are not essential here
String media = null , title
= null, charset = null;
public xml2csv(String xmlFile, String resultFile,
String
delimiterValue) {
try {
// create an instance of the TransformerFactory
class
TransformerFactory tFactory = TransformerFactory.newInstance();
// get the name of the stylesheet that has been defined in
the
XML
file.
// create a Source object for use by the upcoming
newTransformer()
method
Source stylesheet = tFactory.getAssociatedStylesheet (new
StreamSource(xmlFile),media,
title, charset);
// create a transformer which takes the name
of the stylesheet
// as an input parameter
Transformer transformer
= tFactory.newTransformer(stylesheet);
// set a delimiter
via
the setParameter() method of the
transformer
transformer.setParameter("delimiter",delimiterValue);
// perform the transformation
transformer.transform(new
StreamSource(xmlFile), new
StreamResult(resultFile));
System.out.println("Done!");
} catch (TransformerException e) {
System.err.println("The
following error occured: " + e);
}
}
// everything starts
here
public static void main (String[] args) {
if(args.length != 3)
{
System.err.println("Please specify three parameters:n1. The
name
and
path to the XML file.n2. The name of the output file.n3. The
delimiter to
be used.");
return;
}
// set some variables
xmlFile
= args[0];
resultFile = args[1];
delimiterValue = args[2];
xml2csv
mySecondExample = new xml2csv(xmlFile, resultFile,
delimiterValue);
}
}
Most of the code is identical to the previous example. There are a couple of important differences, though. First, the parameters passed to the constructor are different in this case.
Over here, I'm passing three parameters to the constructor: the name of the XML
file, the name of the output file, and the delimiter to be used between the various elements of a record. What about the XSLT file, you ask? Well, that's sourced automatically from the XML file via the getAssociatedStylesheet() method of the TransformerFactory class.
// create an instance of the TransformerFactory class
TransformerFactory
tFactory = TransformerFactory.newInstance();
// get the name
of the stylesheet that has been defined in the
XML file.
// create a
Source object for use by the upcoming
newTransformer() method
Source
stylesheet = tFactory.getAssociatedStylesheet (new
StreamSource(xmlFile),media,
title, charset); [/code]
A new Source object is created to represent this stylesheet; this Source
object
is ultimately passed to the Transformer class.
// create a transformer which takes the name of the stylesheet
// as an input parameter
Transformer transformer = tFactory.newTransformer(stylesheet);
If you take a close look at the XSLT file above, you'll see that I'vedefined an XSLT parameter named "delimiter". This parameter isessentially a variable which can be accessed by XSLT, and a value can beassigned to it via the setParameter() method of the Transformer class.
// set a delimiter via the setParameter() method of the
transformer
transformer.setParameter("delimiter",delimiterValue);
In this case, the "delimiter" parameter is set to whatever delimiter wasspecified by the user.
Finally, the actual transformation is performed, the output stored inthe desired output file, and a result code generated.[code]// perform the transformation transformer.transform(new StreamSource(xmlFile), newStreamResult(resultFile)); System.out.println("Done!");
Here's what it looks like, assuming you use a pipe (|) as delimiter (I haven't used a comma simply because some of the entries already contain commas):
Bugs Bunny|The Rabbit Hole, The Field behind Your
House||bugs@bunnyplanet.com|
Batman|The
Batcave, Gotham City|123
7654|bruce@gotham-millionaires.com|http://www.belfry.net/