Saturday, November 8, 2008

Jasper Reports and legacy formats

I'm not sure when exactly or between which versions, but at some point Jasper Report designs moved from being based on legacy DTD to the more modern XSD schema. It turns out that most tools today, i.e. the iReport plugin for NetBeans, are now overwriting the DTD information of legacy designs in favor of XSD information.
This can cause problems if and when you are not able to change the Jasper engine itself that fills out the template designs. It also did not seem possible to simply plug-in another XML parser and I found it rather hard in general to find information about this and support forums to turn to. So I sought another solution.

If you try to run an XSD based design against the Jasper engine that can only deal with DTD, you will get an error along the line of this:

net.sf.jasperreports.engine.JRException: org.xml.sax.SAXParseException: Document root element "jasperReport", must match DOCTYPE root "null".


By using a legacy designer which favors DTD's (iReport 3.0 standalone) and the recent NetBeans plugin which favors XSD's, it becomes apparent what the exact differences are. Obviously the DTD version has a doctype clause:

<!DOCTYPE jasperReport PUBLIC "//JasperReports//DTD Report Design//EN" "http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">


Furthermore, the jasperReport root element has the extra attributtes:

xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd


One could just edit the files manually, but seriously, what fun is that. Instead, we can create a decorator to perform this job on-the-fly as we send the design off to the Jasper engine to be compiled. We would need to do the following:
  1. Read the InputStream into a DOM tree
  2. Add the missing DTD doctype
  3. Remove the undesired attributtes
  4. Write the dom back out to an InputStream
The result of doing the above I've placed in LegacyJasperInputStream.java.

An example on how to use it:


JasperDesign design = JRXmlLoader.load(
       
new LegacyJasperInputStream(new FileInputStream("MyXsdBasedDesign.jrxml"))
       
);



Probably I am not the only one facing this issue, which is why I chose to write a small entry about it and hopefully helping others. Be advised, you're going to need the slf4j logging facility, or modify the source slightly to make use of the standard java.util.Logger.

Post a Comment