在Java开发的报表工具FineReport中,假如在目录下保存了几个XML文件,希望把XML文件转换为报表数据源,同时希望展示动态xml数据源的效果,这时可通过参数的方式,动态获取xml字段中的值再作为报表数据源。
Northwind.xml记录数据格式如下:
<?xml version="1.0" encoding="UTF-8"?> <Northwind> <Customers> <CustomerID>ALFKI</CustomerID> <CompanyName>ALfreds Futterkiste</CompanyName> <ContactName>Maria Anders</ContactName> <ContactTitle>Sales Representative</ContactTitle> <Address>Obere Str.57</Address> <City>Berlin</City> <PostalCode>12209</PostalCode> <Country>Germany</Country> <Phone>030-0074321</Phone> <Fax>030-0076545</Fax> </Customers> </Northwind>
最终用于制作报表的数据源形式如下:
对于这样的情况我们如何来实现呢?FineReport中可以通过自定义程序数据集来对xml字段数据进行解析,最终返回所希望的数据表。实现步骤如下:
1、 定义XMLColumnNameType4Demo封装类
首先定义参数name及type,供其他类直接调用,安全性比较高,详细代码如下:
package com.fr.data; public class XMLColumnNameType4Demo { private int type = -1; private String name = null; public XMLColumnNameType4Demo(String name, int type) { this.name = name; this.type = type; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getType() { return type; } public void setType(int type) { this.type = type; } }
2、定义XMLParseDemoDataModel.java类文件
定义XMLParseDemoDataModel.java类继承AbstractDataModel接口,实现getColumnCount、getColumnName、getRowCount、getValueAt四个方法,详细代码如下:
package com.fr.data; import java.io.File; import java.util.ArrayList; import java.util.List; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import com.fr.base.FRContext; import com.fr.data.AbstractDataModel; import com.fr.general.ComparatorUtils; import com.fr.general.data.TableDataException; /** * XMLParseDemoDataModel * * DataModel是获取数据的接口 * * 这里通过init方法一次性取数后,构造一个二维表对象来实现DataModel的各个取数方法 */ public class XMLParseDemoDataModel extends AbstractDataModel { // 数据类型标识 public static final int COLUMN_TYPE_STRING = 0; public static final int COLUMN_TYPE_INTEGER = 1; public static final int COLUMN_TYPE_BOOLEAN = 2; // 缓存取出来的数据 protected List row_list = null; // 数据对应的节点路径 private String[] xPath; // 节点路径下包含的需要取数的节点 private XMLColumnNameType4Demo[] columns; private String filePath; public XMLParseDemoDataModel(String filename, String[] xPath, XMLColumnNameType4Demo[] columns) { this.filePath = filename; this.xPath = xPath; this.columns = columns; } /** * 取出列的数量 */ public int getColumnCount() throws TableDataException { return columns.length; } /** * 取出相应的列的名称 */ public String getColumnName(int columnIndex) throws TableDataException { if (columnIndex < 0 || columnIndex >= columns.length) return null; String columnName = columns[columnIndex] == null ? null : columns[columnIndex].getName(); return columnName; } /** * 取出得到的结果集的总的行数 */ public int getRowCount() throws TableDataException { this.init(); return row_list.size(); } /** * 取出相应位置的值 */ public Object getValueAt(int rowIndex, int columnIndex) throws TableDataException { this.init(); if (rowIndex < 0 || rowIndex >= row_list.size() || columnIndex < 0 || columnIndex >= columns.length) return null; return ((Object[]) row_list.get(rowIndex))[columnIndex]; } /** * 释放一些资源,取数结束后,调用此方法来释放资源 */ public void release() throws Exception { if (this.row_list != null) { this.row_list.clear(); this.row_list = null; } } /** ************************************************** */ /** ***********以上是实现DataModel的方法*************** */ /** ************************************************** */ /** ************************************************** */ /** ************以下为解析XML文件的方法**************** */ /** ************************************************** */ // 一次性将数据取出来 protected void init() throws TableDataException { if (this.row_list != null) return; this.row_list = new ArrayList(); try { // 使用SAX解析XML文件, 使用方法请参见JAVA SAX解析 SAXParserFactory f = SAXParserFactory.newInstance(); SAXParser parser = f.newSAXParser(); parser.parse(new File(XMLParseDemoDataModel.this.filePath), new DemoHandler()); } catch (Exception e) { e.printStackTrace(); FRContext.getLogger().error(e.getMessage(), e); } } /** * 基本原理就是解析器在遍历文件时 发现节点开始标记时,调用startElement方法 读取节点内部内容时,调用characters方法 * 发现节点结束标记时,调用endElement */ private class DemoHandler extends DefaultHandler { private List levelList = new ArrayList(); // 记录当前节点的路径 private Object[] values; // 缓存一条记录 private int recordIndex = -1; // 当前记录所对应的列的序号,-1表示不需要记录 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { // 记录下 levelList.add(qName); if (isRecordWrapTag()) { // 开始一条新数据的记录 values = new Object[XMLParseDemoDataModel.this.columns.length]; } else if (needReadRecord()) { // 看看其对应的列序号,下面的characters之后执行时,根据这个列序号来设置值存放的位置。 recordIndex = getColumnIndex(qName); } } public void characters(char[] ch, int start, int length) throws SAXException { if (recordIndex > -1) { // 读取值 String text = new String(ch, start, length); XMLColumnNameType4Demo type = XMLParseDemoDataModel.this.columns[recordIndex]; Object value = null; if (type.getType() == COLUMN_TYPE_STRING) { value = text; } if (type.getType() == COLUMN_TYPE_INTEGER) { value = new Integer(text); } else if (type.getType() == COLUMN_TYPE_BOOLEAN) { value = new Boolean(text); } values[recordIndex] = value; } } public void endElement(String uri, String localName, String qName) throws SAXException { try { if (isRecordWrapTag()) { // 一条记录结束,就add进list中 XMLParseDemoDataModel.this.row_list.add(values); values = null; } else if (needReadRecord()) { recordIndex = -1; } } finally { levelList.remove(levelList.size() - 1); } } // 正好匹配路径,确定是记录外部的Tag private boolean isRecordWrapTag() { if (levelList.size() == XMLParseDemoDataModel.this.xPath.length && compareXPath()) { return true; } return false; } // 需要记录一条记录 private boolean needReadRecord() { if (levelList.size() == (XMLParseDemoDataModel.this.xPath.length + 1) && compareXPath()) { return true; } return false; } // 是否匹配设定的XPath路径 private boolean compareXPath() { String[] xPath = XMLParseDemoDataModel.this.xPath; for (int i = 0; i < xPath.length; i++) { if (!ComparatorUtils.equals(xPath[i], levelList.get(i))) { return false; } } return true; } // 获取该字段的序号 private int getColumnIndex(String columnName) { XMLColumnNameType4Demo[] nts = XMLParseDemoDataModel.this.columns; for (int i = 0; i < nts.length; i++) { if (ComparatorUtils.equals(nts[i].getName(), columnName)) { return i; } } return -1; } } }
3、定义程序数据集XMLDemoTableData
通过参数filename,动态显示xml文件内容,首先xml文件需要放到某个目录下,如下代码是放到D盘,并且定义需要解析的数据列,这边定义的数据列名称,根xml内字段名称是一一对用的。详细代码如下:
packagecom.fr.data; importjava.io.BufferedInputStream; importjava.io.ByteArrayInputStream; importjava.io.File; importjava.io.FileInputStream; importjava.io.FileNotFoundException; importjava.io.FileReader; importjava.io.InputStream; importjava.io.Reader; importjava.util.*; importjavax.xml.stream.XMLEventReader; importjavax.xml.stream.XMLInputFactory; importjavax.xml.stream.XMLStreamException; importjavax.xml.stream.events.XMLEvent; importcom.fr.base.Parameter; importcom.fr.data.AbstractParameterTableData; importcom.fr.general.data.DataModel; importcom.fr.script.Calculator; importcom.fr.stable.ParameterProvider; importcom.fr.stable.StringUtils; /** * XMLDemoTableData * * 这是一个按参数来解析不同地址XML文件的demo * * AbstractParameterTableData 包装了有参数数据集的基本实现 */ publicclass XMLDemoTableData extends AbstractParameterTableData { // 构造函数 public XMLDemoTableData() { // 定义需要的参数,这里定义一个参数,参数名为filename,给其一个默认值"Northwind.xml" this.parameters = newParameter[1]; this.parameters[0] = newParameter("filename", "Northwind"); } /** * 返回获取数据的执行对象 * 系统取数时,调用此方法来返回一个获取数据的执行对象 * 注意!当数据集需要根据不同参数来多次取数时,此方法在一个计算过程中会被多次调用。 */ @SuppressWarnings("unchecked") public DataModel createDataModel(Calculatorcalculator) { // 获取传进来的参数 ParameterProvider[] params =super.processParameters(calculator); // 根据传进来的参数,等到文件的路径 String filename = null; for (int i = 0; i < params.length;i++) { if (params[i] == null)continue; if("filename".equals(params[i].getName())) { filename =(String)params[i].getValue(); } } String filePath; if (StringUtils.isBlank(filename)){ filePath ="D://DefaultFile.xml"; } else { filePath = "D://" +filename + ".xml"; } // 定义需要解析的数据列,机器 // XMLColumnNameType4Demo[] columns = newXMLColumnNameType4Demo[7]; // columns[0] = newXMLColumnNameType4Demo("CustomerID",XMLParseDemoDataModel.COLUMN_TYPE_STRING); // columns[1] = newXMLColumnNameType4Demo("CompanyName",XMLParseDemoDataModel.COLUMN_TYPE_STRING); // columns[2] = newXMLColumnNameType4Demo("ContactName",XMLParseDemoDataModel.COLUMN_TYPE_STRING); // columns[3] = newXMLColumnNameType4Demo("ContactTitle",XMLParseDemoDataModel.COLUMN_TYPE_STRING); // columns[4] = newXMLColumnNameType4Demo("Address",XMLParseDemoDataModel.COLUMN_TYPE_STRING); // columns[5] = newXMLColumnNameType4Demo("City",XMLParseDemoDataModel.COLUMN_TYPE_STRING); // columns[6] = new XMLColumnNameType4Demo("Phone",XMLParseDemoDataModel.COLUMN_TYPE_STRING); List list=new ArrayList(); XMLInputFactory inputFactory =XMLInputFactory.newInstance(); InputStream in; try { in = new BufferedInputStream(newFileInputStream(new File(filePath))); XMLEventReader reader =inputFactory.createXMLEventReader(in); readCol(reader,list); in.close(); } catch (Exception e) { // TODO Auto-generated catchblock e.printStackTrace(); } XMLColumnNameType4Demo[]columns=(XMLColumnNameType4Demo[])list.toArray(newXMLColumnNameType4Demo[0]); // 定义解析的数据在xml文件结构中的位置 String[] xpath = new String[2]; xpath[0] = "Northwind"; xpath[1] = "Customers"; /* * 说明 * 提供的样例xml文件的格式是: * <Notrhwind> * <Customers> * <CustomerID>ALFKI</CustomerID> * <CompanyName>AlfredsFutterkiste</CompanyName> * <ContactName>MariaAnders</ContactName> * <ContactTitle>SalesRepresentative</ContactTitle> * <Address>Obere Str. 57</Address> * <City>Berlin</City> * <PostalCode>12209</PostalCode> * <Country>Germany</Country> * <Phone>030-0074321</Phone> * <Fax>030-0076545</Fax> * </Customers> * </Northwind> * * 上面定义的意义就是 * /Northwind/Customers路径所表示的一个Customers节点为一条数据,它包含的节点中的CustomerID...等等是需要获取的列值 */ // 构造一个实际去取值的执行对象 return new XMLParseDemoDataModel(filePath,xpath, columns); } private int deep=0; private static final int COL_DEEP=3; private boolean flag=false; private void readCol(XMLEventReader reader,List list) throws XMLStreamException { while (reader.hasNext()) { XMLEvent event =reader.nextEvent(); if (event.isStartElement()) { //deep是控制层数的,只把xml中对应的层的加入到列名中 deep++; //表示已经进入到了列名那一层 if(deep==COL_DEEP){ flag=true; } //如果在高层,并且已经进入到了col层,则退出 if(deep<COL_DEEP&&flag){ return; } if(deep!=COL_DEEP){ continue; } // println("name: " +event.asStartElement().getName()); XMLColumnNameType4Democolumn=new XMLColumnNameType4Demo(event.asStartElement().getName().toString(),XMLParseDemoDataModel.COLUMN_TYPE_STRING); list.add(column); readCol(reader,list); } else if (event.isCharacters()){ //对数据值不做处理 } else if (event.isEndElement()){ deep--; return; } } } private void readCol0(XMLEventReader reader) throws XMLStreamException { while (reader.hasNext()) { XMLEvent event = reader.nextEvent(); if (event.isStartElement()) { //deep是控制层数的,只把xml中对应的层的加入到列名中 deep++; //表示已经进入到了列名那一层 if(deep==COL_DEEP){ flag=true; } //如果在高层,并且已经进入到了col层,则退出 if(deep<COL_DEEP&&flag){ return; } if(deep!=COL_DEEP){ continue; } System.out.println("name:" + event.asStartElement().getName()); readCol0(reader); } else if (event.isCharacters()){ //对数据值不做处理 } else if (event.isEndElement()){ deep--; return; } } } public static void main(String[]args){ XMLInputFactory inputFactory =XMLInputFactory.newInstance(); // in = new FileReader(newFile(filePath)); // XMLEventReader reader = inputFactory.createXMLEventReader(in); // readCol(reader,list); BufferedInputStream in; try { in = new BufferedInputStream(newFileInputStream(new File("D:/tmp/f.xml"))); byte[] ba=new byte[3]; in.read(ba,0,3); // System.out.println(in) XMLEventReader reader =inputFactory.createXMLEventReader(in); newXMLDemoTableData().readCol0(reader); } catch (Exception e) { // TODO Auto-generated catchblock e.printStackTrace(); } } }
注:如果xml文件的格式上问题描述处所展示的xml格式不一致,则需要修改类中的deep变量,把列名所在的节点层数改成相对应的数值。
注:XMLDemoTableData里面的filename并不是文件名,而是xml里面某个标签名。
4、编译程序数据源
分别编译XMLColumnNameType4Demo.java、XMLParseDemoDataModel.java、XMLDemoTableData.java三个类文件,将生成的class文件放于%FR_HOME%\WebReport\WEB-INF\classes\com\fr\data下。
5 配置程序数据源
新建报表,模板数据集>程序数据集,选择我们定义好的程序数据集XMLDemoTableData.class文件,名字可以自定义,如程序1。
6、使用程序数据源
在模板数据集窗口,点击预览按钮,弹出参数对话框,输入要显示的xml文件名称,点击确定则可以把Northwind.xml文件里面的数据读取出来转换报表数据源了,如下图:
相关推荐
在Java编程中,处理Excel和XML文件是常见的需求,特别是在数据导入导出、报表生成以及数据分析等场景。本文将深入探讨四个使用Java解析和生成Excel及XML的实例,旨在帮助开发者掌握相关技能。 实例一:解析Excel...
在帆软报表软件FineReport中处理存储于数据库字段中的XML文件,需要对XML数据进行解析,从而将数据转换为可用的报表数据源。XML数据通常以字符串形式存储在数据库表中的某字段内,这种数据结构要求使用者采用特定的...
下面将详细阐述BIRT XML数据源的相关知识点。 1. **BIRT概述**: BIRT是由Eclipse基金会维护的一个开源项目,旨在为Java和Web应用程序提供报表设计和展示功能。它支持多种数据源,包括关系型数据库、XML、Web服务...
在这个场景中,我们关注的是如何使用Java将RPT文件转换为XML文件。RPT文件通常与报表设计工具如Crystal Reports关联,用于存储报告的布局和数据定义。而XML是一种可扩展标记语言,广泛用于数据交换、配置文件以及...
这时,将XML转换为EXCEL就变得非常实用,因为EXCEL的表格形式使得数据查看和编辑更加直观。 在Java中,要实现XML到EXCEL的转换,通常会用到以下技术: 1. **DOM解析器**:Document Object Model (DOM) 是一种用于...
示例代码展示了如何使用Java将数据导出为Excel格式,并通过XML格式定义样式。具体步骤如下: 1. **初始化输出流**:通过`FileOutputStream`和`BufferedOutputStream`创建输出流对象。 2. **构建XML结构**:使用`...
深入解析这一领域,我们需要了解多个关键知识点,包括报表工具的选择、报表设计、数据源处理、动态数据过滤以及报表导出等。 1. **报表工具选择**: 在Java环境中,有许多优秀的报表工具可供选择,例如JFreeReport...
在“利用Zoomline现实报表信息”的场景下,开发者可能需要将数据库或文件中的数据转换为XML格式,因为FusionCharts通常接收XML或JSON作为数据源来生成图表。 描述中提到的“将数据转为XML流”意味着在Java程序中,...
XLStoXML2.0是一款基于Java开发的工具,专门用于将Excel(XLS)文件转换为XML格式。在IT领域,这样的转换器是至关重要的,因为XML是一种数据交换和存储的标准格式,广泛应用于Web服务、数据库交互以及数据集成等场景...
3. **数据连接**:水晶报表可以连接多种数据源,包括数据库、XML文件、Web服务等。在Java应用中,我们需要提供适配器或数据源连接信息,使报表能够正确获取数据。 4. **Java API**:SAP提供了Java SDK,包含了一...
总的来说,这个实现利用了Java的定时器功能,创建了一个每5分钟运行一次的任务,该任务负责从数据源提取信息,并将这些信息格式化为XML文件,供标签云应用读取和显示。这种定时更新的机制确保了数据的实时性和新鲜度...
1. 解析XML源文档:首先,需要使用DOM或SAX解析器读取XML文件,将其转化为内存中的数据结构。 2. 加载XSLT样式表:接着,加载XSLT文件,这将解析样式表并创建转换器对象。 3. 应用转换:然后,使用转换器对象和XML...
帆软报表的PDF导出功能允许用户将报表转换为高质量的PDF文件,便于打印、分发和长期保存。PDF导出过程中,帆软会处理报表的字体、图像和页面设置,确保在任何设备上都能准确呈现。 在压缩包中的"帆软报表导出各种...
以JasperReports为例,这是一个广泛使用的Java报表库,它支持多种数据源(如JDBC、CSV、XML),可以处理多种格式的输出(PDF、HTML、Excel、CSV等)。开发者可以通过设计JRXML文件来定义报表的布局和样式,然后使用...
Java水晶报表开发主要涉及到使用Crystal Reports 2008与JBuilder 2005工具在Java环境中构建和展示报表的全...这样的技能对于提供数据可视化和业务洞察至关重要,尤其在需要将大量数据转化为易于理解的报表的场景下。
Java报表到Office文档转换是Java开发中的一个关键技术领域,主要涉及到如何将通过Java生成的报表数据有效地导出为常见的Office格式,如Word和Excel。这项技术在企业级应用、数据分析以及报告自动化等方面有着广泛的...
ExcelToXml是一个将Excel数据转换为XML格式的工具或库,它可以帮助开发者高效地处理大量结构化的Excel数据,将其转化为更便于程序处理的XML格式。这个资源包含了源代码,这意味着你可以深入理解其内部工作原理,并...
水晶报表在Java Web中的实现是将复杂的业务数据转化为可视化、易于理解的报表的过程。这个实现主要涉及了几个关键技术和组件,包括Crystal Reports、Java、JSP(Java Server Pages)以及Tomcat服务器。以下是对这些...
在XML上下文中,可能涉及到从XML数据源中提取信息并用作报表的数据。 总结,DOM解析XML是Java中处理XML的一种常见方法,尤其适合于需要对整个文档进行深度处理的情况。然而,对于大文件,应考虑使用SAX或StAX等更轻...