DynamicJasper:如何用数组/列表替代预定义对象作为数据源?
Great question! The JRBeanCollectionDataSource is built for JavaBean collections (where each row maps to an object with defined getters/setters), but you absolutely can handle dynamic array-based data in DynamicJasper. Here are two straightforward solutions tailored to your scenario:
Option 1: Use JRMapCollectionDataSource (Simplest Approach)
Instead of wrapping your arrays into predefined objects, convert each array row into a Map<String, Object> where the keys match your report column names, and values are the corresponding array elements (strings, integers, dates, etc.). DynamicJasper works seamlessly with JRMapCollectionDataSource, which is designed for map-based data.
Example Code:
import net.sf.jasperreports.engine.data.JRMapCollectionDataSource; // ... other imports // Step 1: Prepare your dynamic data as a list of maps List<Map<String, Object>> dynamicData = new ArrayList<>(); // Row 1: Map keys match the column names we'll define later Map<String, Object> row1 = new HashMap<>(); row1.put("Region", "West"); row1.put("Sales", 45000); row1.put("SaleDate", new Date()); dynamicData.add(row1); // Row 2 Map<String, Object> row2 = new HashMap<>(); row2.put("Region", "East"); row2.put("Sales", 62000); row2.put("SaleDate", new Date()); dynamicData.add(row2); // Step 2: Build your dynamic report with columns mapped to map keys FastReportBuilder drb = new FastReportBuilder(); DynamicReport dr = drb.addColumn("Region", "Region", String.class.getName(), 40) .addColumn("Sales Amount", "Sales", Integer.class.getName(), 70, true) .addColumn("Transaction Date", "SaleDate", Date.class.getName(), 80) .setTitle("Dynamic Sales Report") .setUseFullPageWidth(true) .build(); // Step 3: Use JRMapCollectionDataSource instead of JRBeanCollectionDataSource JRDataSource ds = new JRMapCollectionDataSource(dynamicData); JasperPrint jp = DynamicJasperHelper.generateJasperPrint(dr, new ClassicLayoutManager(), ds); JasperViewer.viewReport(jp);
If your raw data is in Object[] format, just loop through your arrays and populate the maps using your column names as keys (e.g., for an array rowArray and column list columnNames, do rowMap.put(columnNames[i], rowArray[i]) for each index i).
Option 2: Create a Custom JRDataSource (For Raw Array Data)
If you want to work directly with Object[] arrays without converting to maps, you can implement a custom JRDataSource that handles array indexing. This gives you full control over how fields are fetched from your arrays.
Example Custom Data Source:
import net.sf.jasperreports.engine.JRDataSource; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.JRField; import java.util.List; import java.util.Map; import java.util.HashMap; public class ArrayDataSource implements JRDataSource { private final List<Object[]> dataRows; private final Map<String, Integer> columnIndexMap; private int currentRowIndex = -1; // Constructor: takes your array data and a list of column names (to map names to array indices) public ArrayDataSource(List<Object[]> dataRows, List<String> columnNames) { this.dataRows = dataRows; this.columnIndexMap = new HashMap<>(); for (int i = 0; i < columnNames.size(); i++) { columnIndexMap.put(columnNames.get(i), i); } } @Override public boolean next() throws JRException { currentRowIndex++; return currentRowIndex < dataRows.size(); } @Override public Object getFieldValue(JRField field) throws JRException { String fieldName = field.getName(); Integer columnIndex = columnIndexMap.get(fieldName); if (columnIndex == null || columnIndex >= dataRows.get(currentRowIndex).length) { return null; // Handle missing fields gracefully } return dataRows.get(currentRowIndex)[columnIndex]; } }
Usage Example:
// Step 1: Prepare your raw array data List<Object[]> arrayData = new ArrayList<>(); arrayData.add(new Object[]{"West", 45000, new Date()}); arrayData.add(new Object[]{"East", 62000, new Date()}); // Step 2: Define your column names (order matches array indices) List<String> columnNames = List.of("Region", "Sales", "SaleDate"); // Step 3: Build the report (column property names match the names in columnNames) FastReportBuilder drb = new FastReportBuilder(); DynamicReport dr = drb.addColumn("Region", "Region", String.class.getName(), 40) .addColumn("Sales Amount", "Sales", Integer.class.getName(), 70, true) .addColumn("Transaction Date", "SaleDate", Date.class.getName(), 80) .setTitle("Raw Array Sales Report") .setUseFullPageWidth(true) .build(); // Step 4: Use your custom data source JRDataSource ds = new ArrayDataSource(arrayData, columnNames); JasperPrint jp = DynamicJasperHelper.generateJasperPrint(dr, new ClassicLayoutManager(), ds); JasperViewer.viewReport(jp);
Which Option Should You Choose?
- Go with Option 1 if you want minimal code and don’t mind converting arrays to maps—it’s the quickest and most maintainable approach for dynamic columns.
- Use Option 2 if you need to work directly with raw arrays (e.g., performance constraints or existing array-based data pipelines).
内容的提问来源于stack exchange,提问作者SlimenTN

