javajasper-reportsdynamic-jasper

How to add DynamicJasper report to Jasper Report as sub-report Dynamically


I’m working with a Jasper report in JRXML format, let's say it's mainReport.jrxml. This mainReport contains various fields such as text fields, subreport areas, etc. I’ve add another a subreport in the following manner:

<subreportExpression><![CDATA["subReport.jasper"]]></subreportExpression>

In mainReport.jasper, I aim to include tables with an unpredictable number of columns. If the number of columns were known, I could simply utilize JasperSoft’s built-in table. However, due to the unknown number of columns, I’ve opted to use the Dynamic Jasper library. Following a this suggestion, I’ve successfully created a dynamic Jasper report. Now, my goal is to integrate this Dynamic Jasper report into my mainReport. I attempted to pass this dynamic Jasper report to mainReport as a parameter, but encountered an error. Can anyone provide guidance on how to incorporate a dynamic Jasper report into mainReport?

Here is my JRXML for mainReport

<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Jaspersoft Studio version 6.12.2.final using JasperReports Library version 6.12.2-75c5e90a222ab406e416cbf590a5397028a52de3  -->
<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" name="ncPareto" pageWidth="595" pageHeight="792" whenNoDataType="AllSectionsNoDetail" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="3a2d19d1-8c22-4eb3-a2d0-8ddff71bdb55">
    <property name="com.jaspersoft.studio.data.defaultdataadapter" value="One Empty Record"/>
    <property name="ireport.zoom" value="1.0"/>
    <property name="ireport.x" value="0"/>
    <property name="ireport.y" value="0"/>
    <import value="net.sf.jasperreports.engine.data.JRBeanCollectionDataSource"/>
    <style name="BoldFont" isBold="true"/>
    <parameter name="anotherSubreportDataSource" class="net.sf.jasperreports.engine.data.JRBeanCollectionDataSource"/>
    <parameter name="companyLogo" class="java.awt.image.BufferedImage"/>
    <parameter name="dynamiJasperReport" class="java.lang.Object"/>
    <parameter name="dynamiJasperReportData" class="java.util.Collection"/>
    <queryString>
        <![CDATA[]]>
    </queryString>
    <group name="ReportContents" isReprintHeaderOnEachPage="true">
        <groupHeader>
            <band height="24">
                <subreport overflowType="Stretch">
                    <reportElement positionType="Float" x="0" y="0" width="555" height="20" uuid="c8d669fb-85eb-42d5-8b00-b02e2bcd8e26">
                        <property name="com.jaspersoft.studio.unit.width" value="px"/>
                        <property name="com.jaspersoft.studio.unit.height" value="px"/>
                    </reportElement>
                    <dataSourceExpression><![CDATA[$P{anotherSubreportDataSource}]]></dataSourceExpression>
                    <subreportExpression><![CDATA["anotherSubreport.jasper"]]></subreportExpression>
                </subreport>
            </band>
        </groupHeader>
    </group>
    <title>
        <band height="275" splitType="Stretch">
            <property name="com.jaspersoft.studio.unit.height" value="px"/>
            <textField pattern="MMMMM dd, yyyy HH:mm">
                <reportElement x="0" y="80" width="130" height="18" uuid="08f30fa9-efb3-4215-bfc9-e7a82c503f5c"/>
                <textElement>
                    <font size="12"/>
                </textElement>
                <textFieldExpression><![CDATA[new java.util.Date()]]></textFieldExpression>
            </textField>
            <image>
                <reportElement x="0" y="0" width="551" height="81" uuid="565dabb7-52e2-4462-9653-c70ae0fef6b9"/>
                <imageExpression><![CDATA[$P{companyLogo}]]></imageExpression>
            </image>
            <subreport>
                <reportElement positionType="Float" x="0" y="255" width="555" height="20" isRemoveLineWhenBlank="true" uuid="243c105e-4ccc-4e8b-952b-5cef05cbf648">
                    <property name="com.jaspersoft.studio.unit.width" value="px"/>
                    <property name="com.jaspersoft.studio.unit.height" value="px"/>
                    <property name="com.jaspersoft.studio.unit.y" value="px"/>
                </reportElement>
                <dataSourceExpression><![CDATA[new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($P{dynamiJasperReportData})]]></dataSourceExpression>
                <subreportExpression><![CDATA[$P{dynamiJasperReport}]]></subreportExpression>
            </subreport>
        </band>
    </title>
</jasperReport>

Here dynamiJasperReport is Dynamic Jasper report that was created using Dynamic Jasper Library. And dynamiJasperReportData holds the data for dynami Jasper Report.

Here is how we are filling mainReport

        Pair<JasperReport, Collection> pair =  this.createSubReportForHTMLTable(htmlTableString);
        Map parameters = new HashMap();
        parameters.put("companyLogo", image);
        parameters.put("dynamiJasperReport", pair.getLeft() );
        parameters.put("dynamiJasperReportData",  pair.getRight());
        parameters.put("anotherSubreportDataSource", anotherSubreportDataSource);
JasperPrint jasperPrint = JasperFillManager.fillReport(compiledReportFilePath, parameters,
                    new JREmptyDataSource());
            System.out.println("Report filling time : " + (System.currentTimeMillis() - start));

            if ("PDF".equals(request.getExportType())) {
                reportService.exportToPdf(jasperPrint, response, "MainReport.pdf");
            } 

Here compiledReportFilePath = "C:\Report\mainReport.jasper

Here is how I'm creating dynamic Jasper Report

    public Pair<JasperReport, Collection> createSubReportForHTMLTable(String htmlTable) throws Exception {
        if (htmlTable == null || htmlTable.isEmpty()) {
            return null;
        }
        try {
            Document doc = Jsoup.parse(htmlTable);
            Elements tables = doc.select("table");
            if (tables == null || tables.isEmpty()) {
                return null;
            }
            // Get first table ignore others
            Element table = tables.get(0);
            //Get all rows
            Elements rows = table.select("tr");
            ArrayList<String> columnName = new ArrayList<>();
            Elements headerRow = rows.get(0).select("th, td");

            for (Element header : headerRow) {
                columnName.add(header.text().replace("\"", "'") + "aa");
            }

            DynamicReportBuilder reportBuilder = new DynamicReportBuilder();

            Style rowStyle = new Style();
            rowStyle.setBorderTop(Border.THIN());
            rowStyle.setBorderBottom(Border.THIN());
            rowStyle.setBorderLeft(Border.THIN());
            rowStyle.setBorderRight(Border.THIN());

            for (String column : columnName) {
                AbstractColumn columnBuilder = ColumnBuilder.getNew()
                        .setColumnProperty(column, String.class.getName())
                        .setTitle(column)
                        .setWidth(100)
                        .setStyle(rowStyle)
                        .setHeaderStyle(rowStyle)
                        .build();
                reportBuilder.addColumn(columnBuilder);
            }

            DynamicReport dynamicReport = reportBuilder.build();

            List<Map<String, String>> dataList = new ArrayList<>();

            for (int i = 0; i < rows.size(); i++) {
                Elements columns = rows.get(i).select("td");
                Map<String, String> data = new HashMap<>();
                for (int j = 0; j < columns.size(); j++) {
                    data.put(columnName.get(j), columns.get(j).text().replace("\"", "'") + "aa");
                }
                dataList.add(data);
            }

            Collection collection = new ArrayList<>(dataList);

            JRDataSource dataSource = new JRBeanCollectionDataSource(collection);
            JasperReport jasperReport = DynamicJasperHelper.generateJasperReport(dynamicReport, new ClassicLayoutManager(), new HashMap<>());

            JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, new HashMap<>(), dataSource);
            JasperViewer.viewReport(jasperPrint);
            return new Pair<>(jasperReport, collection);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

Solution

  • you are close. As with every subreport, you need to tell the main report how the datasource to the subreport will be provided.

    Also remember that dynamicJasper uses a custom scripplet class (DJDefaultScriptlet) needed for the dynamic report to properly put some variables in place.

    You might look into dynamicjasper sub-reporting examples, which can give you hints on which parts might be missing.

    Also, DynamicJasperHelper.generateJasperReport(...) method can give you hints on how to wire report and subreports.

    Besides the above, you can use DJ with "templates", where depending on your use case, you might manage to put the "non dyamic" info in some templates bands.

    Or, you can use the "concatenated report technique", where the 1st report is your main report, and then you concatenate the DJ report, somehow mimicking the sub-report output.