Clojure: передача списка записей объекту Java

#groovy #clojure #jasper-reports

Вопрос:

Я экспериментирую с Clojure. Я смог создать отчет в JasperReports, но он дает нулевые значения. Рассматриваемый объект Java таков JRBeanCollectionDataSource . Я передаю ему список записей, но почему-то PDF содержит только нули.

Кроме того, почему я не могу выдать его {} за пустой HashMap ? В Groovy [:] синтаксис работает нормально.

 (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"))
 

Это переписывание следующего рабочего классного решения:

 @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")
 

Отредактируйте Для полностью рабочего примера, поместите следующий файл в текущий рабочий каталог для Groovy и каталог ресурсов для 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>
 

Комментарии:

1. Могу сказать наверняка без полностью воспроизводимого примера, но {} создает экземпляр Map неизменяемого, а это не экземпляр HashMap . Кроме того, [] не создает массив — он создает неизменяемый вектор, который является экземпляром List . Наконец, defrecord создает что-то, что отличается от того, что @Immutable class дает вам, вероятно, в том смысле, который здесь важен.

2. Для полностью работающего примера необходимо использовать один XML-файл; я добавил этот файл. Если бы это было вызвано несоответствием типов, я бы ожидал, что пример завершится ошибкой с исключением.

3. Для меня исключением является: Execution error (UnsupportedOperationException) at net.sf.jasperreports.engine.fill.JRFillDataset/setDatasourceParameterValue (JRFillDataset.java:1291) .

4. @cfrick Я получаю именно это исключение, когда определяю параметры как (def params {}) . Если я использую (def params (HashMap.)) , компиляция работает нормально. Тогда проблема в (def ds (JRBeanCollectionDataSource. data)) том, что данные по какой-то причине неправильно обработаны, я получаю нули в окончательном отчете.

5. Как я вижу в документах JRBeanCollectionDataSource , ожидается сбор JavaBeans. @Immutable создает JavaBean (решающее значение имеют геттеры, см. Здесь ), в то время как запись Clojure не является JB. clj-bean может быть полезен здесь.

Ответ №1:

С помощью подсказок из комментариев я смог решить эту проблему. Проблема в том, что записи Clojure не реализуют спецификацию JavaBeans, в то время JRBeanCollectionDataSource как ожидаются такие компоненты.

С clj-bean помощью библиотеки я смог заставить ее работать.

 (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)])
 

Теперь отчет содержит данные.