ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

XSLT Processing with Java
Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9

Using the parser

To wrap things up, let's look at how you will actually use this CSV parser with an XSLT stylesheet. The code shown in Example 5-8 is a standalone Java application that allows you to perform XSLT transformations on CSV files. As the comments indicate, it requires the name of a CSV file as its first parameter and can optionally take the name of an XSLT stylesheet as its second parameter. All output is sent to System.out.


Example 5-8: SimpleCSVProcessor.java

package com.oreilly.javaxslt.util;
 
import java.io.*;
import javax.xml.transform.*;
import javax.xml.transform.sax.*;
import javax.xml.transform.stream.*;
import org.xml.sax.*;
 
/**
* Shows how to use the CSVXMLReader class.
* This is a command-line utility that takes
* a CSV file and optionally an XSLT file as
* command line parameters. A transformation
* is applied and the output is sent to
* System.out.
*/
public class SimpleCSVProcessor {
 
  public static void main(String[] args) throws Exception {
    if (args.length == 0) {
      System.err.println("Usage: java "
          + SimpleCSVProcessor.class.getName( )
          + " <csvFile> [xsltFile]");
      System.err.println(" - csvFile is required");
      System.err.println(" - xsltFile is optional");
      System.exit(1);
    }
 
    String csvFileName = args[0];
    String xsltFileName = (args.length > 1) ? args[1] : null;
 
    TransformerFactory transFact = TransformerFactory.newInstance( );
    if (transFact.getFeature(SAXTransformerFactory.FEATURE)) {
      SAXTransformerFactory saxTransFact =
          (SAXTransformerFactory) transFact;
      TransformerHandler transHand = null;
      if (xsltFileName == null) {
        transHand = saxTransFact.newTransformerHandler( );
      } else {
        transHand = saxTransFact.newTransformerHandler(
            new StreamSource(new File(xsltFileName)));
      }
 
      // set the destination for the XSLT transformation
      transHand.setResult(new StreamResult(System.out));
 
      // hook the CSVXMLReader to the CSV file
      CSVXMLReader csvReader = new CSVXMLReader( );
      InputSource csvInputSrc = new InputSource(
          new FileReader(csvFileName));
 
      // attach the XSLT processor to the CSVXMLReader
      csvReader.setContentHandler(transHand);
      csvReader.parse(csvInputSrc);
    } else {
      System.err.println("SAXTransformerFactory is not supported.");
      System.exit(1);
    }
  }
}


As mentioned earlier in this chapter, the TransformerHandler is provided by JAXP and is an implementation of the org.xml.sax.ContentHandler interface. It is constructed by the SAXTransformerFactory as follows:

TransformerHandler transHand = null;
if (xsltFileName == null) {
  transHand = saxTransFact.newTransformerHandler(  );
} else {
  transHand = saxTransFact.newTransformerHandler(
    new StreamSource(new File(xsltFileName)));
}

When the XSLT stylesheet is not specified, the transformer performs an identity transformation. This is useful when you just want to see the raw XML output without applying a stylesheet. You will probably want to do this first to see how your XSLT will need to be written. If a stylesheet is provided, however, it is used for the transformation.

The custom parser is then constructed as follows:

CSVXMLReader csvReader = new CSVXMLReader( );

The location of the CSV file is then converted into a SAX InputSource:

InputSource csvInputSrc = new InputSource(
        new FileReader(csvFileName));

And finally, the XSLT processor is attached to our custom parser. This is accomplished by registering the TransformerHandler as the ContentHandler on csvReader. A single call to the parse method causes the parsing and transformation to occur:

// attach the XSLT processor to the CSVXMLReader
csvReader.setContentHandler(transHand);
csvReader.parse(csvInputSrc);

For a simple test, assume that a list of presidents is available in CSV format:

Washington,George,,
Adams,John,,
Jefferson,Thomas,,
Madison,James,,
  etc...
Bush,George,Herbert,Walker
Clinton,William,Jefferson,
Bush,George,W,

To see what the XML looks like, invoke the program as follows:

java com.oreilly.javaxslt.util.SimpleCSVProcessor presidents.csv

This will parse the CSV file and apply the identity transformation stylesheet, sending the following output to the console:

<?xml version="1.0" encoding="UTF-8"?>
<csvFile>
  <line>
    <value>Washington</value>
    <value>George</value>
    <value/>
    <value/>
  </line>
  <line>
    etc...
</csvFile>

Actually, the output is crammed onto a single long line, but it is broken up here to make it more readable. Any good XML editor application should provide a feature to pretty-print the XML as shown. In order to transform this into something useful, a stylesheet is required. The XSLT stylesheet shown in Example 5-9 takes any output from this program and converts it into an HTML table.


Example 5-9: csvToHTMLTable.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>
 
  <xsl:template match="/">
    <table border="1">
      <xsl:apply-templates select="csvFile/line"/>
    </table>
  </xsl:template>
 
  <xsl:template match="line">
    <tr>
      <xsl:apply-templates select="value"/>
    </tr>
  </xsl:template>
 
  <xsl:template match="value">
    <td>
      <!-- If a value is empty, print a non-breaking space
           so the HTML table looks OK -->
      <xsl:if test=".=''">
        <xsl:text>&# disable-output-escaping="yes">&amp;nbsp;</xsl:text>
      </xsl:if>
      <xsl:value-of select="."/>
    </td>
  </xsl:template>
</xsl:stylesheet>

In order to apply this stylesheet, type the following command:

java com.oreilly.javaxslt.util.SimpleCSVProcessor presidents.csv csvToHTMLTable.xslt

As before, the results are sent to System.out and contain code for an HTML table. This stylesheet will work with any CSV file parsed with SimpleCSVProcessor, not just presidents.xml. Now that the concept has been proved, you can add fancy formatting and custom output to the resulting HTML without altering any Java code--just edit the stylesheet or write a new one.

Conclusion

Although writing a SAX parser and connecting it to JAXP does involve quite a few interrelated classes, the resulting application requires only two command line arguments and will work with any CSV or XSLT file. What makes this example interesting is that the same approach will work with essentially any data source. The steps are broken down as follows:

  1. Create a custom SAX parser by implementing org.xml.sax.XMLReader or extending com.oreilly.javaxslt.util.AbstractXMLReader.
  2. In your parser, emit the appropriate SAX events as you read your data.
  3. Modify SimpleCSVProcessor to utilize your custom parser instead of CSVXMLReader.

For example, you might want to write a custom parser that accepts a SQL statement as input rather than a CSV file. Your parser could then connect to a database, issue the query, and fire SAX events for each row in the ResultSet. This makes it very easy to extract data from any relational database without writing a lot of custom code. This also eliminates the intermediate step of JDOM or DOM production because the SAX events are fed directly into JAXP for transformation.

Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9

Next Pagearrow