How to Create a Jasper Report from an XML Datasource: A Step-by-Step Guide
1. Introduction JasperReports is an open-source Java reporting tool that allows developers to create sophisticated reports for display, pri...
https://www.czetsuyatech.com/2024/06/java-how-to-create-jasper-report-from-xml-datasource.html
1. Introduction
JasperReports is an open-source Java reporting tool that allows developers to create sophisticated reports for display, printing, or exporting into a variety of formats such as PDF, HTML, CSV, and others. It's widely used in Java-based applications for generating pixel-perfect reports with rich content like charts, tables, and images. JasperReports provides a flexible and customizable framework for designing and generating reports from various data sources, including databases, XML files, and custom data sources. It's often integrated into Java web applications and enterprise systems for generating dynamic and professional-looking reports.
2. Use Case
In this article, we will take a look at how Opencell an open-source billing platform integrates Jasper reports to generate its invoicing documents.
The goal of this exercise is to:
- Generate a financial document (invoice) using Jasper reports.
- Use a custom font.
- Use an XML document.
Using XML as a data source in JasperReports can be ideal for several reasons:
- Flexibility: XML is a highly flexible data format that can represent structured data in a hierarchical manner. This makes it suitable for a wide range of data sources, including relational databases, web services, and custom data structures.
- Platform-agnostic: XML is platform-independent, meaning it can be easily generated and consumed by different programming languages and technologies. This makes it a versatile choice for integrating with various systems and applications.
- Ease of Integration: Many applications and systems already generate or consume XML data, so using XML as a data source can simplify integration with existing infrastructure.
- Customizability: XML allows developers to define custom data structures and schemas tailored to their specific reporting needs. This enables precise control over the data consumed by JasperReports and the structure of the resulting reports.
- Separation of Concerns: Using XML as a data source promotes a clean separation between data and presentation layers. This separation allows for easier maintenance, scalability, and reuse of report templates across different datasets.
- Performance: When properly optimized, XML processing can offer good performance characteristics for report generation. Techniques such as indexing, caching, and streaming can be employed to efficiently handle large XML datasets.
Overall, XML serves as a versatile and effective data source for JasperReports, offering flexibility, interoperability, and ease of integration with diverse data sources and systems.
3. Coding
We'll develop a Java application to produce a PDF invoice by utilizing a Jasper template sourced from an XML data provider.
3.1 XML Datasource
Our XML document is generated from Opencell that contains customer and usage information.
<invoice customerAccountCode="CA_2000000021" customerId="CUST_2000000021" id="101" invoiceCounter="00000027" number="INV00000027" templateName="invoice" type="INV"> <header> <provider code="CT" description="CZETSUYATECH"> <bankCoordinates> <ics>FRXXZZZ123456</ics> <iban>FR763000600001123456790189</iban> <bic>CTDEFRPPCCT</bic> </bankCoordinates> </provider> <customer brand="" category="CLIENT" code="CUST_2000000021" externalRef1="" externalRef2="" id="565" jobTitle="" registrationNo="" sellerCode="CZETSUYATECH" vatNo=""> <address> <address1/> <address2/> <address3/> <city/> <postalCode/> <state/> <country/> <countryName/> </address> <contact/> </customer> <seller code="CZETSUYATECH" description="CZETSUYATECH"> <address> <address1/> <address2/> <address3/> <city/> <postalCode/> <state/> <country/> <countryName/> </address> <contact/> </seller> <customerAccount accountTerminated="false" code="CA_2000000021" currency="EUR" description="Edward's Account" externalRef1="" externalRef2="" id="566" jobTitle="" language="English" vatNo="xxx"> <contact email="czetsuya@gmail.com" fax="" mobile="" phone="(303) 829-5342"/> <paymentMethod type="CHECK"/> <name> <quality/> <name>Edward's Account</name> </name> <address> <address1>1234 Balamb Garden</address1> <address2/> <address3/> <city>Los Banos</city> <postalCode>4030</postalCode> <state/> <country>PH</country> <countryName>Philippines</countryName> </address> </customerAccount> <billingAccount billingCycleCode="MONTHLY_POST_PAID" code="BA_2000000021" description="BA_2000000021" endPeriod="31/05/2024" externalRef1="" externalRef2="" id="567" jobTitle="" startPeriod="09/05/2024"> <billingCycle code="MONTHLY_POST_PAID" description="Monthly Invoice Cycle" id="6"> </billingCycle> <email>czetsuya@gmail.com</email> <name> <quality/> <name>Edward's Account</name> </name> <address> <address1>1234 Balamb Garden</address1> <address2/> <address3/> <city>Los Banos</city> <postalCode>4030</postalCode> <state/> <country>PH</country> <countryName>Philippines</countryName> </address> <message/> <billTo> <![CDATA[Edward's Account<br/>1234 Balamb Garden<br/>Los Banos, Laguna 4030<br/>PH]]> </billTo> <fees/> <charges licensePlate="NLEDW_LE2021" urlImage="https://czetsuyatech.com/PHEDW_LE2021.latest.jpg"> <charge amount="-161.000000000000" date="05/28/24 19:45" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> <charge amount="161.000000000000" date="5/9/24 9:29 AM" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> <charge amount="161.000000000000" date="5/9/24 9:29 AM" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> <charge amount="161.000000000000" date="5/9/24 9:29 AM" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> <charge amount="161.000000000000" date="5/9/24 9:29 AM" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> <charge amount="161.000000000000" date="5/9/24 9:29 AM" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> <charge amount="161.000000000000" date="5/9/24 9:29 AM" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> <charge amount="161.000000000000" date="5/9/24 9:29 AM" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> <charge amount="161.000000000000" date="5/9/24 9:29 AM" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> <charge amount="161.000000000000" date="5/9/24 9:29 AM" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> <charge amount="161.000000000000" date="5/9/24 9:29 AM" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> <charge amount="161.000000000000" date="5/9/24 9:29 AM" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> <charge amount="161.000000000000" date="5/9/24 9:29 AM" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> <charge amount="161.000000000000" date="5/9/24 9:29 AM" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> <charge amount="161.000000000000" date="5/9/24 9:29 AM" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> <charge amount="161.000000000000" date="5/9/24 9:29 AM" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> <charge amount="161.000000000000" date="5/9/24 9:29 AM" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> <charge amount="161.000000000000" date="5/9/24 9:29 AM" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> <charge amount="161.000000000000" date="5/9/24 9:29 AM" description="GoPlus" transactionId="566170"/> <charge amount="161.000000000000" date="5/10/24 7:52 AM" description="GoPlus" transactionId="566171"/> </charges> <topups/> </billingAccount> <invoiceDate>13/05/2024</invoiceDate> <dueDate>13/06/2024</dueDate> <paymentMethod>CHECK</paymentMethod> <comment> </comment> <categories> <category code="CLASSIC_USAGE" label="Service Usage"> <amountWithoutTax>322.000000000000</amountWithoutTax> <subCategories> <subCategory amountWithoutTax="322.000000000000" code="CLASSIC_USAGE" label="Service Usage" taxCode="TAX_00" taxPercent="0.000000000000"/> </subCategories> </category> </categories> <discounts/> <locale>en-US</locale> </header> <amount> <currency>EUR</currency> <amountWithoutTax>322.000000000000</amountWithoutTax> <amountWithTax>322.000000000000</amountWithTax> <netToPay>322.000000000000</netToPay> <taxes total="0.000000000000"> <tax code="TAX_00" id="1"> <name>0 Percent Tax</name> <percent>0.000000000000</percent> <amount>0.000000000000</amount> <amountHT>322.000000000000</amountHT> </tax> </taxes> </amount> <detail> <userAccounts> <userAccount code="UA_2000000021" description="UA_2000000021" id="568" jobTitle=""> <customFields> <customField code="CUSTOMER_NUMBER" description="Customer Number">1190017814</customField> <customField code="ACCOUNT_NUMBER" description="Account Number">2000000021</customField> </customFields> <subscriptions> <subscription code="SUBS_2000000021_NLEDW_LE2021_1590017830" description="SUBS_2000000021_NLEDW_LE2021_1590017830" id="309" offerCode="GoPlus"> <subscriptionDate>13/10/2023</subscriptionDate> <endAgreementDate/> </subscription> </subscriptions> <name> <quality/> <firstName>Ed</firstName> <name/> </name> <address> <address1/> <address2/> <address3/> <city/> <postalCode/> <state/> <country/> <countryName/> </address> <categories> <category code="CLASSIC_USAGE" label="Service Usage"> <amountWithoutTax>322.000000000000</amountWithoutTax> <subCategories> <subCategory amountWithoutTax="322.000000000000" code="CLASSIC_USAGE" label="Service Usage" taxCode="TAX_00" taxPercent="0.000000000000"> <line code="GoPlus_CT" param1="fad4c36b-215a-308d-a19d-b6074543d414:fad4c36b-215a-308d-a19d-b6074543d414" param2="1" param3="566170"> <walletOperation code="GoPlus_CT" periodEndDate="" periodStartDate=""/> <paramExtra>USAGE</paramExtra> <pricePlan code="GoPlus_CT" description="Price Plan for GoPlus CT"/> <label> GoPlus||EUR|1.0|0.00|161.00|161.00|0E-12|161.000000000000|161.000000000000|USE_TRX_PRICE </label> <unitAmountWithoutTax>161.000000000000</unitAmountWithoutTax> <amountWithoutTax>161.000000000000</amountWithoutTax> <quantity>1.000000000000</quantity> <usageDate>09/05/2024</usageDate> <edr accessCode="AP_2000000021_NLEDW_LE2021" dateParam1="" dateParam2="" dateParam3="" dateParam4="" dateParam5="" decimalParam1="161.000000000000" decimalParam2="" decimalParam3="" decimalParam4="" decimalParam5="" eventDate="2024-05-09T09:29:46" originBatch="API_192.168.67.123" originRecord="opencell.admin_1715247534640" parameter1="fad4c36b-215a-308d-a19d-b6074543d414:fad4c36b-215a-308d-a19d-b6074543d414" parameter2="1" parameter3="566170" parameter4="CHARGE_GoPlus_CT" parameter5="VTOLL" parameter6="NLEDW_LE2021" parameter7="" parameter8="USE_TRX_PRICE" parameter9="" quantity="1.000000000000" rejectReason="" status="RATED" subscription="SUBS_2000000021_NLEDW_LE2021_1590017830"/> </line> <line code="GoPlus_CT" param1="fad4c36b-215a-308d-a19d-b6074543d414:fad4c36b-215a-308d-a19d-b6074543d414" param2="1" param3="566171"> <walletOperation code="GoPlus_CT" periodEndDate="" periodStartDate=""/> <paramExtra>USAGE</paramExtra> <pricePlan code="GoPlus_CT" description="Price Plan for GoPlus CT"/> <label> GoPlus||EUR|1.0|0.00|161.00|161.00|0E-12|161.000000000000|161.000000000000|USE_TRX_PRICE </label> <unitAmountWithoutTax>161.000000000000</unitAmountWithoutTax> <amountWithoutTax>161.000000000000</amountWithoutTax> <quantity>1.000000000000</quantity> <usageDate>10/05/2024</usageDate> <edr accessCode="AP_2000000021_NLEDW_LE2021" dateParam1="" dateParam2="" dateParam3="" dateParam4="" dateParam5="" decimalParam1="161.000000000000" decimalParam2="" decimalParam3="" decimalParam4="" decimalParam5="" eventDate="2024-05-10T07:52:12" originBatch="API_192.168.67.60" originRecord="opencell.admin_1715327638732" parameter1="fad4c36b-215a-308d-a19d-b6074543d414:fad4c36b-215a-308d-a19d-b6074543d414" parameter2="1" parameter3="566171" parameter4="CHARGE_GoPlus_CT" parameter5="VTOLL" parameter6="NLEDW_LE2021" parameter7="" parameter8="USE_TRX_PRICE" parameter9="" quantity="1.000000000000" rejectReason="" status="RATED" subscription="SUBS_2000000021_NLEDW_LE2021_1590017830"/> </line> </subCategory> </subCategories> </category> </categories> </userAccount> <userAccount description="-"> <categories/> </userAccount> </userAccounts> </detail> <offers> <offer code="GoPlus" description="GoPlus" id="1"> <customFields> <customField code="CF_SHARED" description="Offer Shared">INDIVIDUAL</customField> </customFields> </offer> </offers> <services> <service code="CT" description="CT" offerCode="GoPlus"/> </services> <priceplans> <priceplan code="GoPlus_CT" description="Price Plan for GoPlus CT"> </priceplan> </priceplans> <orders/> <qrCode value="xxx"/> <invoiceQRCodeEmail value="xxx"/> <summary currentBalance="-644.00" outstandingBalance="0.00" previousBalance="0.00" totalAmountDue="322.00"/> </invoice>
As we can see the root of our document is
"invoice"
.
3.2 Jasper Dependencies
<dependency> <groupId>net.sf.jasperreports</groupId> <artifactId>jasperreports</artifactId> <version>6.21.0</version> </dependency> <dependency> <groupId>jaxen</groupId> <artifactId>jaxen</artifactId> <version>1.2.0</version> </dependency> <dependency> <groupId>org.apache.groovy</groupId> <artifactId>groovy</artifactId> <version>4.0.21</version> </dependency> <dependency> <groupId>net.sf.jasperreports</groupId> <artifactId>jasperreports-fonts</artifactId> <version>6.20.0</version> </dependency>
3.3 Java Code
package com.czetsuyatech; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.net.URLClassLoader; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import net.sf.jasperreports.engine.DefaultJasperReportsContext; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.JRParameter; import net.sf.jasperreports.engine.JRPropertiesUtil; import net.sf.jasperreports.engine.JasperExportManager; import net.sf.jasperreports.engine.JasperFillManager; import net.sf.jasperreports.engine.JasperPrint; import net.sf.jasperreports.engine.JasperReport; import net.sf.jasperreports.engine.data.JRXmlDataSource; import net.sf.jasperreports.engine.util.JRLoader; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.xml.sax.SAXException; public class InvoiceReportGenerator { private final String jasperReportsPath; private ClassLoader cl; private String INVOICE_TAG_NAME = "invoice"; public InvoiceReportGenerator(String jasperReportsPath, URLClassLoader cl) { this.jasperReportsPath = jasperReportsPath; this.cl = cl; } public void generate(String jasperPath, String xmlDataSource) throws IOException, ParserConfigurationException, SAXException, TransformerException, JRException { File jasperFile = new File(jasperReportsPath, jasperPath); String pdfFullFilename = jasperReportsPath + "/output.pdf"; File invoiceXmlFile = new File(jasperReportsPath, xmlDataSource); Map<String, Object> parameters = getParameters(); InputStream reportTemplate = new FileInputStream(jasperFile); Node invoiceNode = getInvoiceNode(invoiceXmlFile); JRXmlDataSource dataSource = new JRXmlDataSource(getJasperReportContext(invoiceNode), "/" + INVOICE_TAG_NAME); Map<String, JasperReport> jasperReportMap = new HashMap<>(); String fileKey = jasperFile.getPath() + jasperFile.lastModified(); JasperReport jasperReport = jasperReportMap.get(fileKey); if (jasperReport == null) { jasperReport = (JasperReport) JRLoader.loadObject(reportTemplate); jasperReportMap.put(fileKey, jasperReport); } DefaultJasperReportsContext context = DefaultJasperReportsContext.getInstance(); JRPropertiesUtil.getInstance(context).setProperty("net.sf.jasperreports.xpath.executer.factory", "net.sf.jasperreports.engine.util.xml.JaxenXPathExecuterFactory"); JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, dataSource); JasperExportManager.exportReportToPdfFile(jasperPrint, pdfFullFilename); } private Node getInvoiceNode(File invoiceXmlFile) throws TransformerException, ParserConfigurationException, IOException, SAXException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); dbf.setNamespaceAware(true); Document xmlDocument = db.parse(invoiceXmlFile); xmlDocument.getDocumentElement().normalize(); Node invoiceNode = xmlDocument.getElementsByTagName(INVOICE_TAG_NAME).item(0); Transformer trans = TransformerFactory.newInstance().newTransformer(); trans.setOutputProperty(OutputKeys.INDENT, "yes"); StringWriter writer = new StringWriter(); trans.transform(new DOMSource(xmlDocument), new StreamResult(writer)); return invoiceNode; } private ByteArrayInputStream getJasperReportContext(Node invoiceNode) throws TransformerException { return new ByteArrayInputStream(getNodeXmlString(invoiceNode).getBytes( StandardCharsets.UTF_8)); } private Map<String, Object> getParameters() { return new HashMap<>() {{ put(JRParameter.REPORT_CLASS_LOADER, cl); }}; } protected String getNodeXmlString(Node node) throws TransformerException { TransformerFactory transFactory = TransformerFactory.newInstance(); Transformer transformer = transFactory.newTransformer(); StringWriter buffer = new StringWriter(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); transformer.transform(new DOMSource(node), new StreamResult(buffer)); return buffer.toString(); } }
Post a Comment