You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将DynamicJasper报表动态嵌入Jasper主报表作为子报表

动态Jasper报表集成到主Jasper报表的解决方案

问题背景

我正在使用JRXML格式的Jasper报表(mainReport.jrxml),报表包含文本字段、子报表等元素。需要嵌入列数不确定的表格,因列数未知选择Dynamic Jasper库创建动态报表,但尝试将其作为参数传递给主报表时出错,需指导正确集成方式。

现有代码问题分析

  1. 主报表中dynamiJasperReport参数类型设为java.lang.Object,缺乏类型约束易引发转换错误
  2. 子报表数据源重复创建,可能导致数据结构不匹配
  3. 动态报表列宽固定为100,易超出主报表可用宽度(555)引发布局溢出
  4. 动态报表数据收集逻辑未跳过表头,导致表头被当作数据行重复添加

修正步骤

  1. 明确主报表中动态报表参数的类型为net.sf.jasperreports.engine.JasperReport
  2. 直接传递已构建的JRDataSource给主报表,避免重复创建数据源
  3. 根据主报表可用宽度动态计算列宽,适配不同列数的表格
  4. 修正数据收集逻辑,跳过表头行只读取数据内容

完整修正代码

修正后的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; // 抛出异常便于上层处理
    }
}

验证要点

  1. 确保Dynamic Jasper与JasperReports版本兼容(如Dynamic Jasper 5.1.0对应JasperReports 6.x系列)
  2. 测试不同列数的HTML表格,验证列宽自适应和布局正确性
  3. 确认子报表overflowType设为Stretch,确保内容超出时自动扩展

内容的提问来源于stack exchange,提问作者ahasun chy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 09:44:49