4
votes

Currently I'm using JasperReports to generate pdfs displaying a list of payments, one entity per page. Code for that (for one entity):

 Map<String, Object> parameters = new HashMap<String, Object>();
 JasperReport jasperReport = (JasperReport) JRLoader.loadObjectFromFile(path + "WEB-INF/jasper/PaymentOrder.jasper");
                report = JasperFillManager.fillReport(jasperReport, parameters, new JREmptyDataSource());

What I need to do is to display two enitities in one file. I'm trying to use subreports to do that.

  1. How can I pass a Map with parameters to my subreport?
  2. And how to I change my subreport file (if needed) to get parameters from parent?
  3. How can I pass ResourceBundle from master report to subreport?
  4. How can I set the name of subreport?

Master report:

<?xml version="1.0" encoding="UTF-8"?>
<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="report name" pageWidth="595" pageHeight="842" columnWidth="535" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20">
    <title>
        <band height="531" splitType="Stretch">
            <textField isBlankWhenNull="true">
                <reportElement x="0" y="0" width="555" height="20"/>
                <textElement verticalAlignment="Middle">
                    <font fontName="Times New Roman" isUnderline="false" pdfEncoding="UTF-8" isPdfEmbedded="true"/>
                </textElement>
                <textFieldExpression><![CDATA["* MASTER REPORT *"]]></textFieldExpression>
            </textField>
            <subreport runToBottom="false">
                <reportElement positionType="Float" x="0" y="55" width="555" height="15" isPrintInFirstWholeBand="true" isPrintWhenDetailOverflows="true"/>
                <dataSourceExpression><![CDATA[$P{REPORT_PARAMETERS_MAP}.get( "subReport1Params" )]]></dataSourceExpression>
                <subreportExpression><![CDATA[$P{REPORT_PARAMETERS_MAP}.get( "subReport1" )]]></subreportExpression>
            </subreport>
        </band>
    </title>
</jasperReport> 

Subreport:

<?xml version="1.0" encoding="UTF-8"?>
<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="report name" pageWidth="595" pageHeight="842" columnWidth="535" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20">
    <title>
        <band height="531" splitType="Stretch">
            <textField isBlankWhenNull="true">
                <reportElement x="0" y="0" width="555" height="20"/>
                <textElement verticalAlignment="Middle">
                    <font fontName="Times New Roman" isUnderline="false" pdfEncoding="UTF-8" isPdfEmbedded="true"/>
                </textElement>
                <textFieldExpression><![CDATA[$P{REPORT_RESOURCE_BUNDLE}.getString( "user" ) + ": "+$P{REPORT_PARAMETERS_MAP}.get( "user" )]]></textFieldExpression>
            </textField>
            <textField isBlankWhenNull="true">
                <reportElement x="0" y="22" width="555" height="22"/>
                <textElement textAlignment="Center" verticalAlignment="Middle">
                    <font fontName="Times New Roman" isUnderline="false" pdfEncoding="UTF-8" isPdfEmbedded="true"/>
                </textElement>
                <textFieldExpression><![CDATA[$P{REPORT_RESOURCE_BUNDLE}.getString( "payment" ) + " № "+$P{REPORT_PARAMETERS_MAP}.get( "docNumber" )+" "+$P{REPORT_RESOURCE_BUNDLE}.getString( "from" ) + " "+$P{REPORT_PARAMETERS_MAP}.get( "date" )]]></textFieldExpression>
            </textField>
        </band>
    </title>
</jasperReport>

java code:

HashMap<String, Object> parameters = getParametersForPayment(doc1, user, locale);
String path = ((WebApplication) Application.get()).getServletContext().getRealPath("/");

File fileSubReport1 = new File(path + "WEB-INF/jasper/PaymentOrder.jasper");
parameters.put("subReport1", JRLoader.loadObject(fileSubReport1));


File f = new File(path + "WEB-INF/jasper/PaymentMaster.jasper");
JasperReport jasperReport = (JasperReport) JRLoader.loadObject(f);
report = JasperFillManager.fillReport(jasperReport, parameters, new JREmptyDataSource());
2
@AlexK, the first one is not suitable for me because I don't have a list of same objects, they are different. I need to dynamically load jasper files based on types of objects I have (for example, paymentA.jasper for paymentA, paymentB.jasper for paymentB). In the second they are passing REPORT_DATA_SOURCE from main to subreport. But I am not getting data from JRDataSource, I am getting data from Hashmap (i.e. $P{REPORT_PARAMETERS_MAP}.get( "amount" ))user1328889
And what is the problem you faced? You can use subreportExpression + pass REPORT_PARAMETERS_MAP to subreportAlex K
@AlexK I tried <dataSourceExpression><![CDATA[$P{REPORT_PARAMETERS_MAP}]]></dataSourceExpression>, the error is 'java.util.HashMap cannot be cast to net.sf.jasperreports.engine.JRDataSource'user1328889
You should post the small sample (main report + subreport)Alex K

2 Answers

8
votes

Questions

How can I pass a Map with parameters to my subreport?

You are right, you can pass parameters via REPORT_PARAMETERS_MAP parameter or with help of subreportParameter, subreportParameterExpression.

How can I set the name of subreport?

You can use subreportExpression with parameters.

How can I pass ResourceBundle from master report to subreport?

In case you are passing the REPORT_PARAMETERS_MAP to the subreport you do not need to do anything else.


You was so close to solve your task. Your mistakes are:

  1. You did not set the resourceBundle attribute for the report - to use the $P{REPORT_RESOURCE_BUNDLE}
  2. You did not pass REPORT_PARAMETERS_MAP parameter to the subreport with help of parametersMapExpression - to use parameters in subreport

Another recommendation is too add parameters declaration in template. It is easier to support reports in this case and it is a good style.


The working example

The main report and two subreports will be used in example. The name of the subreport to use will be passed via parameters from Java code. I'll add internationalization support for en_US locale. Only parameters will be used for showing data (without any datasource).

Resource bundle

The small resource bundle master_en_US.properties is using.

user=User:
payment=Payment:
from=From:

Master report's template

There are two ways if we are passing ALL parameters from the Java code: we can declare parameters in jrxml file or we can skip this step. As I said, I prefer the variant with declaration in template (first case).

In first case the template will be:

<?xml version="1.0" encoding="UTF-8"?>
<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="master" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" resourceBundle="master" whenNoDataType="AllSectionsNoDetail">
    <parameter name="user" class="java.lang.String" isForPrompting="false"/>
    <parameter name="docNumber" class="java.lang.String"/>
    <parameter name="date" class="java.lang.String"/>
    <parameter name="subReportName" class="java.lang.String">
        <defaultValueExpression><![CDATA["subreport_1"]]></defaultValueExpression>
    </parameter>
    <title>
        <band height="241" splitType="Stretch">
            <subreport>
                <reportElement x="0" y="20" width="326" height="59" uuid="f629d1c8-658f-4ae0-a492-2912a7868e96"/>
                <parametersMapExpression><![CDATA[$P{REPORT_PARAMETERS_MAP}]]></parametersMapExpression>
                <connectionExpression><![CDATA[$P{REPORT_CONNECTION}]]></connectionExpression>
                <subreportExpression><![CDATA[$P{subReportName} + ".jasper"]]></subreportExpression>
            </subreport>
        </band>
    </title>
</jasperReport>

And in the second case:

<?xml version="1.0" encoding="UTF-8"?>
<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="master" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" resourceBundle="master" whenNoDataType="AllSectionsNoDetail">
    <title>
        <band height="241" splitType="Stretch">
            <subreport>
                <reportElement x="0" y="20" width="326" height="59" uuid="f629d1c8-658f-4ae0-a492-2912a7868e96"/>
                <parametersMapExpression><![CDATA[$P{REPORT_PARAMETERS_MAP}]]></parametersMapExpression>
                <connectionExpression><![CDATA[$P{REPORT_CONNECTION}]]></connectionExpression>
                <subreportExpression><![CDATA[$P{REPORT_PARAMETERS_MAP}.get("subReportName") + ".jasper"]]></subreportExpression>
            </subreport>
        </band>
    </title>
</jasperReport>

Subreports

The one subreport will be with parameters declaration block and another one - without.

The subreport_1.jrxml:

<?xml version="1.0" encoding="UTF-8"?>
<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="subreport_1" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" whenNoDataType="AllSectionsNoDetail">
    <parameter name="user" class="java.lang.String" isForPrompting="false"/>
    <parameter name="docNumber" class="java.lang.String"/>
    <parameter name="date" class="java.lang.String"/>
    <title>
        <band height="79" splitType="Stretch">
            <textField>
                <reportElement x="0" y="10" width="400" height="15" />
                <textElement textAlignment="Left"/>
                <textFieldExpression><![CDATA[$P{REPORT_RESOURCE_BUNDLE}.getString("user") + " " + $P{user}]]></textFieldExpression>
            </textField>
            <textField>
                <reportElement x="0" y="25" width="400" height="15" />
                <textElement textAlignment="Left"/>
                <textFieldExpression><![CDATA[$P{REPORT_RESOURCE_BUNDLE}.getString("payment") + " № " + $P{docNumber} + " " +
$P{REPORT_RESOURCE_BUNDLE}.getString("from") + " " + $P{date}]]></textFieldExpression>
            </textField>
        </band>
    </title>
</jasperReport>

The subreport_2.jrxml:

<?xml version="1.0" encoding="UTF-8"?>
<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="subreport_2" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" whenNoDataType="AllSectionsNoDetail">
    <title>
        <band height="79" splitType="Stretch">
            <textField>
                <reportElement x="0" y="10" width="400" height="15" />
                <textElement textAlignment="Center" verticalAlignment="Middle"/>
                <textFieldExpression><![CDATA[$P{REPORT_RESOURCE_BUNDLE}.getString("user") + " " + $P{REPORT_PARAMETERS_MAP}.get("user")]]></textFieldExpression>
            </textField>
        </band>
    </title>
</jasperReport>

The Java code

String subreportName;
// some code
JasperReport jasperReport;
try (InputStream inputStream = JRLoader.getResourceInputStream("master.jrxml")) {
    jasperReport = JasperCompileManager.compileReport(JRXmlLoader.load(inputStream));
}

Map<String, Object> params = new HashMap<>();
params.put(JRParameter.REPORT_LOCALE, Locale.US);
params.put("user", "Bruce Wayne");
params.put("docNumber", "Some #");
params.put("date", "03.06.2010");
if (!isNullOrEmpty(subreportName)) {
    params.put("subReportName", subreportName); // the default name in this case will be set in master report (<defaultValueExpression><![CDATA["subreport_1"]]></defaultValueExpression>) 
}
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, params, new JREmptyDataSource());

Output result

I've used the PDFExporter and in case of subreport_1 the result will be:

The generated pdf file

in case of subreport_2 the result will be:

The generated pdf file


Notes:

0
votes
  1. In main (Parent file)

  2. In sub (child file) enter code here

  3. In Java file ResourceBundle resourceBundle = ResourceBundle.getBundle("application", new LOCALE("en"));

HashMap parameters = new HashMap(); parameters.put("REPORT_RESOURCE_BUNDLE", resourceBundle);
JasperFillManager.fillReport(report, parameters, source);