如何将DynamicJasper报表动态嵌入Jasper主报表作为子报表
动态Jasper报表集成到主Jasper报表的解决方案
问题背景
我正在使用JRXML格式的Jasper报表(mainReport.jrxml),报表包含文本字段、子报表等元素。需要嵌入列数不确定的表格,因列数未知选择Dynamic Jasper库创建动态报表,但尝试将其作为参数传递给主报表时出错,需指导正确集成方式。
现有代码问题分析
- 主报表中
dynamiJasperReport参数类型设为java.lang.Object,缺乏类型约束易引发转换错误 - 子报表数据源重复创建,可能导致数据结构不匹配
- 动态报表列宽固定为100,易超出主报表可用宽度(555)引发布局溢出
- 动态报表数据收集逻辑未跳过表头,导致表头被当作数据行重复添加
修正步骤
- 明确主报表中动态报表参数的类型为
net.sf.jasperreports.engine.JasperReport - 直接传递已构建的
JRDataSource给主报表,避免重复创建数据源 - 根据主报表可用宽度动态计算列宽,适配不同列数的表格
- 修正数据收集逻辑,跳过表头行只读取数据内容
完整修正代码
修正后的mainReport.jrxml
<?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"/> <!-- 修正参数类型为明确的JasperReport --> <parameter name="dynamiJasperReport" class="net.sf.jasperreports.engine.JasperReport"/> <parameter name="dynamiJasperDataSource" class="net.sf.jasperreports.engine.JRDataSource"/> <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 overflowType="Stretch"> <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[$P{dynamiJasperDataSource}]]></dataSourceExpression> <subreportExpression><![CDATA[$P{dynamiJasperReport}]]></subreportExpression> </subreport> </band> </title> </jasperReport>
修正后的主报表填充代码
Pair<JasperReport, JRDataSource> pair = this.createSubReportForHTMLTable(htmlTableString); Map<String, Object> parameters = new HashMap<>(); parameters.put("companyLogo", image); parameters.put("dynamiJasperReport", pair.getLeft()); parameters.put("dynamiJasperDataSource", 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"); }
修正后的动态报表创建代码
public Pair<JasperReport, JRDataSource> 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; } Element table = tables.get(0); Elements rows = table.select("tr"); if (rows.size() < 1) { return null; } ArrayList<String> columnNames = new ArrayList<>(); Elements headerCells = rows.get(0).select("th, td"); for (Element cell : headerCells) { columnNames.add(cell.text().replace("\"", "'")); } DynamicReportBuilder reportBuilder = new DynamicReportBuilder(); Style rowStyle = new Style(); rowStyle.setBorderTop(Border.THIN()); rowStyle.setBorderBottom(Border.THIN()); rowStyle.setBorderLeft(Border.THIN()); rowStyle.setBorderRight(Border.THIN()); // 动态计算列宽:主报表可用宽度555,均分列数 int columnWidth = 555 / columnNames.size(); for (String column : columnNames) { AbstractColumn columnBuilder = ColumnBuilder.getNew() .setColumnProperty(column, String.class.getName()) .setTitle(column) .setWidth(columnWidth) .setStyle(rowStyle) .setHeaderStyle(rowStyle) .build(); reportBuilder.addColumn(columnBuilder); } DynamicReport dynamicReport = reportBuilder.build(); List<Map<String, String>> dataList = new ArrayList<>(); // 跳过表头行,从第2行开始读取数据 for (int i = 1; i < rows.size(); i++) { Elements cells = rows.get(i).select("td"); Map<String, String> data = new HashMap<>(); for (int j = 0; j < cells.size() && j < columnNames.size(); j++) { data.put(columnNames.get(j), cells.get(j).text().replace("\"", "'")); } dataList.add(data); } JRDataSource dataSource = new JRBeanCollectionDataSource(dataList); JasperReport jasperReport = DynamicJasperHelper.generateJasperReport(dynamicReport, new ClassicLayoutManager(), new HashMap<>()); return new Pair<>(jasperReport, dataSource); } catch (Exception e) { e.printStackTrace(); throw e; // 抛出异常便于上层处理 } }
验证要点
- 确保Dynamic Jasper与JasperReports版本兼容(如Dynamic Jasper 5.1.0对应JasperReports 6.x系列)
- 测试不同列数的HTML表格,验证列宽自适应和布局正确性
- 确认子报表
overflowType设为Stretch,确保内容超出时自动扩展
内容的提问来源于stack exchange,提问作者ahasun chy
相关产品推荐
相关产品推荐

