Using JasperReports With An ArrayList of Objects As The Data Source, Part 1, Creating the Report
Introduction
I recently had to research how to use JasperReports (refer. 1) to produce PDF, rich text format (RTF), and Excel reports using an ArrayList collection of Java objects as the source for the data in the report. The main Java class has as one of its attributes an ArrayList collection of another Java object. I had the challenge of figuring out how to include all the data from the nested ArrayList collection in the report. After several frustrating hours researching this issue in the very limited JasperReports documentation and on the JasperReports forums I found a hint that lead me to a solution.
Since there does not appear to be very good documentation on how to use complex collections as the data source for a JasperReport, I decided to write a blog entry that will hopefully help others in the future (and of course remind me how I did this when I have to do it again in six months).
JasperReports
If you visit the JasperReports website you can find information about what JasperReports does. Basically it's a comprehensive package of Java classes that enables you to use various sources as data used to create a variety of report types including PDF, Excel, and RTF. There is an open-source community edition of JasperReports that you can download (refer. 2). As of July 6, the version was JasperReports 3.0.0.
After you download and unzip the community edition of JasperReports you'll have a folder named JasperReports-3.0.0. In this folder are sample applications, quick start documentation, and the jar files needed to use JasperReports. The main jar you'll need to include in your Java project is jasperreports-3.0.0.jar, located in the sub-folder dist.
There is some documentation for JasperReports available at reference 3.
iReport
In conjunction with JasperReports you can use iReport to graphically design your reports (instead of writing the report XML manually [the .jrxml file]). iReport gives you the ability to specify a data source and then drag-and-drop the fields from that data source onto the report. You can download iReport at reference 4. iReport is free to download and use. There is a Windows installer version and as of July 6, the iReport version is also 3.0.0.
There is some documentation for iReport available at reference 5.
Learning How To Use JasperReports and iReport
From my research there is limited free documentation available (references 3 and 5) on the JasperReport website. There are some books you can purchase for learning how to use JasperReports and iReport at the JasperReport website. You can also Google JasperReports tutorial for a list of tutorials that may help you get started.
The general steps to creating a report are
- In iReport specify a source of the data that will be used to fill the report.
- Once iReport has access to this data source, you design the report.
- User iReport to compile that report design into a file of type.jasper.
- The .jasper file can then be used by JasperReports to be filled with data which creates a .jrprint file type.
- The .jrprint file type can then be used by JasperReports to create a PDF, RTF, or Excel version of the file.
An Example of Using An ArrayList As A Data Source For JasperReports
JasperReports and iReport can use data from various sources including databases and collections. For my current project, the data source is a collection of objects. To give you an example of how to use JasperReports and iReport to create a report that uses an ArrayList of objects as the data source I've prepared the following example. Example source code in Eclipse project.
Model Classes:
We have two model classes: Person and Phone. A Person has a firstName and lastName. Since a Person may have multiple phones, a Person also has an ArrayList of Phone objects.
A Phone has a phoneType (for example "work" or "mobile") and a phoneNumber.
You can download an Eclipse Java project with the source code for Person and Phone and a test class from reference 6.
Setup the Data Source in iReport
If you examine the source code in test.TestPerson class (reference 6) you'll see that I've created a static method named getBeanCollection that returns an ArrayList of Person objects. This method is used in iReport to specify the data source and then will be used to fill the report with Person objects.
To create our report in iReport, we need to give iReport access to our class files for Person and Phone so that iReport can find their fields. Then we can drag-and-drop the fields onto a report design. So I created a jar of the model package (which includes the Person and Phone classes) and placed the jar in the iReport lib folder, which on my system is located at: C:\Program Files\JasperSoft\iReport-3.0.0\lib\.
Now after restarting iReport, iReport will have access to the attributes of the Person and Phone class.
After starting up iReport we also need to specify the path to the class that has the getBeanCollection method, in this case that class is test.TestPerson and on my system the path to the test package is C:\coursecatalogtest\TestJasperReport\bin. So I put that path in iReport - Options - ClassPath - Add Folder.
To setup the data source in iReport go to Data - Connections/Data Sources - New. Select JavaBeans set data source. Then specify the following:
Name: PersonDataSource
Factory class: test.TestPerson
The static method to call to retrieve... getBeanCollection
You should be able to click on the Test button and get the message "Connection Test Successful." Click on Save to save this data source.
Create A Report in iReport
Use File - New Document and give the report a name of contacts. The click on OK
You'll now have a blank report with areas for title, pageHeader, columnHeader, detail, columnFooter, pageFooter, lastPageFooter, and summary. For our example we will just be placing static text in the title and columnHeader's area and then our Person fields in the detail area. For each Person object in the ArrayList, there will be a corresponding section in the detail area.
Accessing the Person Class Fields
To access the model class fields (in our example Person and Phone) in the report, click on Data - Report Query. The click on the JavaBean Data Source tab and enter the class name with path: model.Person. Because you created a jar file of the model package and placed it in iReport's lib folder earlier iReport can find the class and its attributes.
Click on the firstName, lastName, and phones attributes and then click on Add Selected Fields. Then click on OK.
Next click on View - Fields and you'll see these attributes. Drag the firstName and lastName fields to the detail area of the report. In the detail area you should see two boxes, one with $F{firstName} and one with $F{lastName}. The firstName and lastName fields (F) from each Person object in the collection will be placed into the detail area.
Save your report.
Accessing the Phone Class Fields
Because the phones attribute of the Person class is an ArrayList containing Phone objects, we will need to handle displaying all the Phone objects for each Person by using a sub-report. Click on Edit - Insert Element - Subreport. Use the cross-hair cursor to drag a rectangle in the detail area below where you placed the firstName and lastName fields.
With the create new report option checked click on Next. The Connection/Data Source should be PersonDataSource and the JavaBean class is model.Phone. Click next.
You should see the Phone class fields, phoneNumber and phoneType. Select both of those and click on the arrow to move them from the left box to the right box. Click Next.
Select either columnar layout or tabular layout and report format (I choose Tabular layout and classicT.xml). Click Next.
Click Finish.
You should see a subreport with the $F{phoneNumber} and $F(phoneType) fields in the detail area and the static text phoneNumber and phoneType in the columnHeader area of the subreport.
Save your reports.
Specify the Data Source for the Sub Report
Double click on the contacts.jrxml in the Files window to return to the main report. In the main report will be a sub-report icon inside the rectangle you drew earlier. Right click on the sub-report icon and select properties. Click on the Subreport tab.
In the Connection/Data Source Expression drop down box select "Use data source expression."
Click on the "open expression editor button" that is just below the drop down box. In the expression editor, delete any text in the window and type in the following: new JRBeanCollectionDataSource($F{phones}). What this means is that for the sub-report you want to use a new bean collection with the phones field of the Person class as the source of the beans.
Click on Check Expression button and you should get the message: "Expression successfully validated."
Click on Apply and then close the sub-report properties window.
Add Title and Column Headings
You can add static text to any area of the report by clicking on Edit - Insert Element - Static Text. Do so and then using the cross-hair cursor draw a rectangle text area in the title area of the report. Then double click on the rectangle and type in a title (eg "Contacts"). You can then use teh font family and size drop downs to style the text. Do the same procedure to add static text to the column headings area (eg "Name and Phones").
Test the Report
To test the report, you need to compile the main and sub-report and then execute the report.
Before compiling the report, set the location of where you want the compiled files to be placed. Click on Options and then settings and then click on the compiler tab. Click on the browse button and select a folder where you want the to save the compiled .jrxml report files (which are the .jasper files). I choose "C:\JasperReports".
Click on Build - Compile to compile the main contacts.jrxml file and then do the same after double-clicking on the subreport file. If you look inside the folder where you told iReport to place the compiled files you should see two files: contacts.jasper and contacts_subreport0.jasper. The .jasper files are the ones JasperReports can use to fill with data from the data source.
Double click on contacts.jrxml in iReport to open that file back up. The click on Build - Execute (with active connection). In the surreport_dir parameter window enter the folder where you told iReport to save the compiled files (in my example "c:\JasperReports\").
The iReport Jasper viewer should open up with all the Person objects displayed in the report. For each Person object there should be Phone objects displayed.
Summary
We created a very simple main and sub reports using an ArrayList of Person objects. The sub-report is tied to each Person object through the phones ArrayList (a collection of Phone objects) that is an attribute of each Person object.
iReport can be used to create very complex reports. Each report element can be styled with a specifc font, size, color, etc.
What's Next?
In part 2 we'll examine how to use the .jasper files (the compiled report files) in a Java application.
I read your columns with interest as I too am looking at using JasperReports and iReport. I'd like to repeat your test case. Can you post the jrxml file?
thanks
Thank you very much for your great effort to put everything together as a real sample. Recently, my project needs a report. I looked around and fund jsperReport may fit my struts2 webapps. As a new comer, your papers are very helpful.
When I follow your paper to lunch iReport, I had the error: Can't find the translation for key=gui.elementpropertiessheet.sheetProperty.renderType: using default (Render type). After setup my database and fill out Report Wizar, I had an empty report page without cursor. Nothing can be inserted. I don't know if you had this problem or not when you setup the iReport. Any suggestion would be great appriciated.
Ching
Then after installing, try to just create a basic report using my first tutorial as an example.
The tutorial is so good but I can't get it done using with subreport. With that, I tried creating simple report, no subreport. I modified the Person object and removed the Phone object on it. I followed your instruction using DataConnection, and reading the Person object. I put the properties (firstname, lastname) in the "detail" part and it works. After that, I didn't implement the subreport.
Then, I tried running it on servlet to be exported for PDF file but it didn't work. Below is the code that I used. I was just confused if I need to put key's in the map. I changed the SUBREPORT_DIR to REPORT_DIR, vice versa, and both didn't work.
Sir, could you provide me a sample using only simple/basic report (no subreport)?
String jasper_dir = "C:/jasperreports/";
Map parameters = new HashMap();
//parameters.put("REPORT_DIR", jasper_dir); //formerly SUBREPORT_DIR
JasperFillManager.fillReportToFile(jasper_dir +"OneReport.jasper", parameters, new JRBeanCollectionDataSource(TestPerson.getBeanCollection()));
JasperExportManager.exportReportToPdfFile(jasper_dir +"OneReport.jrprint");
//The updated Person Object in the TestPerson
public static ArrayList<Person> getBeanCollection() {
ArrayList<Person> people = new ArrayList<Person>();
Person person = new Person("Bruce", "Phillips");
people.add(person);
person = new Person("Tom", "Jackson");
people.add(person);
person = new Person("Sue", "Smith" );
people.add(person);
return people;
}
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Thanks and Best Regards,
Bajun
Thanks very much for your hard work and clear, concise info.
I've been using JasperReports for a few years, and have [apparently] been unnecessarily gyrating my data to present complex data structures.
Your site helps a ton.
Best,
-eric-
Clear, precise and it works.
Thanks.
Guillaume.
JasperCompileManager.compileReportToFile("/test/contacts.jrxml");
I got this error:
SEVERE: Parse Error at line 35 column -1: Value "java.util.ArrayList" is not one of the enumerated values for this attribute.
org.xml.sax.SAXParseException: Value "java.util.ArrayList" is not one of the enumerated values for this attribute.
What did I do wrong here? Thanks!
Here is part of my contacts.jrxml file:
<import value="java.util.ArrayList" />
<import value="net.sf.jasperreports.engine.*" />
<import value="net.sf.jasperreports.engine.data.*" />
<parameter name="SUBREPORT_DIR" isForPrompting="true" class="java.lang.String">
<defaultValueExpression ><![CDATA["C:\\XOworkspace\\DOC_SERVER_VIEW\\docServer\\JasReptServer\\war\\WEB-INF\\docSrvConfigDEV\\config\\JasperConfig\\"]]></defaultValueExpression>
</parameter>
<field name="firstName" class="java.lang.String">
<fieldDescription><![CDATA[firstName]]></fieldDescription>
</field>
<field name="lastName" class="java.lang.String">
<fieldDescription><![CDATA[lastName]]></fieldDescription>
</field>
<field name="phones" class="java.util.ArrayList">
<fieldDescription><![CDATA[phones]]></fieldDescription>
</field>
I am just a beginner to Jasper and struts 2. I was searching for the tips to user jasper report in struts web app. Your first step in
creating the jasper reports with sub-report really helped me a lot saved lot of my time. I have successfully viewed the outputs.
However, the sub-report shows blank detail. I think somewhere the link between the main and sub-report is lost.
Thanks a lot.
~Kannan, Singapore.
try to solve it....