I am experimenting with Clojure. I was able to generate a report in JasperReports, but
it gives null values. The Java object in question is JRBeanCollectionDataSource
.
I pass it a list of records, but somehow, the PDF contains only nulls.
Also, why am I not able to pass it {}
as an empty HashMap
? In Groovy, the [:]
syntax works OK.
(ns jasper.core
(:import
(net.sf.jasperreports.engine JasperCompileManager
JasperFillManager
JasperExportManager)
(net.sf.jasperreports.engine.data JRBeanCollectionDataSource)))
(import 'java.util.HashMap)
(defrecord Car [id name price])
(def data [(->Car 1, "Audi", 52642)
(->Car 2, "Mercedes", 57127)
(->Car 3, "Skoda", 9000)
(->Car 4, "Volvo", 29000)
(->Car 5, "Bentley", 350000)
(->Car 6, "Citroen", 21000)
(->Car 7, "Hummer", 41400)
(->Car 8, "Volkswagen", 21600)])
(def xmlFile "resources/report.xml")
(def jrReport (JasperCompileManager/compileReport xmlFile))
;; (def params {})
(def params (HashMap.))
(def ds (JRBeanCollectionDataSource. data))
(println (.toString ds))
(def jrPrint (JasperFillManager/fillReport jrReport params ds))
(defn -main
[]
(JasperExportManager/exportReportToPdfFile jrPrint "report.pdf"))
This is a rewrite of the following working Groovy solution:
@Grab(group='net.sf.jasperreports', module='jasperreports', version='6.17.0')
import net.sf.jasperreports.engine.JasperCompileManager
import net.sf.jasperreports.engine.JasperFillManager
import net.sf.jasperreports.engine.JasperExportManager
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource
import groovy.transform.Immutable
@Immutable
class Car {
Long id;
String name;
int price;
}
def data = [
new Car(1L, 'Audi', 52642),
new Car(2L, 'Mercedes', 57127),
new Car(3L, 'Skoda', 9000),
new Car(4L, 'Volvo', 29000),
new Car(5L, 'Bentley', 350000),
new Car(6L, 'Citroen', 21000),
new Car(7L, 'Hummer', 41400),
new Car(8L, 'Volkswagen', 21600),
]
def empty = []
def xmlFile = "report.xml"
def jrReport = JasperCompileManager.compileReport(xmlFile)
def ds = new JRBeanCollectionDataSource(data)
def params = [:]
def jrPrint = JasperFillManager.fillReport(jrReport, params, ds)
JasperExportManager.exportReportToPdfFile(jrPrint, "report.pdf")
Edit For a fully working example, place the following file in the current working directory for Groovy, and resources directory for Clojure.
<?xml version = "1.0" encoding = "UTF-8"?>
<!DOCTYPE jasperReport PUBLIC "//JasperReports//DTD Report Design//EN"
"http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">
<jasperReport 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"
whenNoDataType="NoDataSection"
name="report" topMargin="20" bottomMargin="20">
<field name="id" class="java.lang.Long"/>
<field name="name"/>
<field name="price" class="java.lang.Integer"/>
<detail>
<band height="15">
<textField>
<reportElement x="0" y="0" width="50" height="15"/>
<textElement textAlignment="Right" verticalAlignment="Middle"/>
<textFieldExpression class="java.lang.Long">
<![CDATA[$F{id}]]>
</textFieldExpression>
</textField>
<textField>
<reportElement x="150" y="0" width="100" height="15" />
<textElement textAlignment="Left" verticalAlignment="Middle"/>
<textFieldExpression class="java.lang.String">
<![CDATA[$F{name}]]>
</textFieldExpression>
</textField>
<textField>
<reportElement x="200" y="0" width="100" height="15" />
<textElement textAlignment="Right" verticalAlignment="Middle"/>
<textFieldExpression class="java.lang.Integer">
<![CDATA[$F{price}]]>
</textFieldExpression>
</textField>
</band>
</detail>
<noData>
<band height="15">
<staticText>
<reportElement x="0" y="0" width="200" height="15"/>
<box>
<bottomPen lineWidth="1.0" lineColor="#CCCCCC"/>
</box>
<textElement />
<text><![CDATA[The report has no data]]></text>
</staticText>
</band>
</noData>
</jasperReport>
With the hints from the comments, I was able to resolve the issue. The problem is that Clojure records do not implement the JavaBeans spec, while the JRBeanCollectionDataSource
expects such beans.
With the clj-bean
library, I was able to make it work.
(defbean Car
[[Long id]
[String name]
[Integer price]])
(def data [(Car. 1 "Audi" 52642),
(Car. 2 "Mercedes" 57127),
(Car. 3 "Skoda" 9000),
(Car. 4 "Volvo" 29000),
(Car. 5 "Bentley" 350000),
(Car. 6 "Citroen" 21000),
(Car. 7 "Hummer" 41400),
(Car. 8 "Volkswagen" 21600)])
Now the report contains data.