开发者社区> 技术小阿哥> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

Java如何解析某个目录下xml文件,将XML文件转换为报表数据源?

简介:
+关注继续查看

Java开发的报表工具FineReport中,假如在目录下保存了几个XML文件,希望把XML文件转换为报表数据源,同时希望展示动态xml数据源的效果,这时可通过参数的方式,动态获取xml字段中的值再作为报表数据源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Northwind.xml记录数据格式如下:
<?xmlversion="1.0" encoding="UTF-8"?>
<Northwind>
    <Customers>
            <CustomerID>ALFKI</CustomerID>
                   <CompanyName>ALfredsFutterkiste</CompanyName>
                   <ContactName>MariaAnders</ContactName>
                   <ContactTitle>SalesRepresentative</ContactTitle>
                   <Address>ObereStr.57</Address>
                   <City>Berlin</City>
                   <PostalCode>12209</PostalCode>
                   <Country>Germany</Country>
                   <Phone>030-0074321</Phone>
                   <Fax>030-0076545</Fax>
         </Customers>
</Northwind>


最终用于制作报表的数据源形式如下:

wKioL1f_R1Lh8oAdAAAhI26URh8078.png

对于这样的情况我们如何来实现呢?FineReport中可以通过自定义程序数据集来对xml字段数据进行解析,最终返回所希望的数据表。实现步骤如下:

1、定义XMLColumnNameType4Demo封装类

首先定义参数nametype,供其他类直接调用,安全性比较高,详细代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
packagecom.fr.data;  
   
publicclass 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接口,实现getColumnCountgetColumnNamegetRowCountgetValueAt四个方法,详细代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
packagecom.fr.data;  
   
importjava.io.File;  
importjava.util.ArrayList;  
importjava.util.List;  
importjavax.xml.parsers.SAXParser;  
importjavax.xml.parsers.SAXParserFactory;  
importorg.xml.sax.Attributes;  
importorg.xml.sax.SAXException;  
importorg.xml.sax.helpers.DefaultHandler;  
importcom.fr.base.FRContext; 
importcom.fr.data.AbstractDataModel;  
importcom.fr.general.ComparatorUtils;
importcom.fr.general.data.TableDataException;
   
/**
 * XMLParseDemoDataModel 
 *  
 * DataModel是获取数据的接口 
 *  
 * 这里通过init方法一次性取数后,构造一个二维表对象来实现DataModel的各个取数方法 
 */  
publicclass 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(Stringfilename, String[] xPath,  
            XMLColumnNameType4Demo[] columns){  
        this.filePath = filename;  
        this.xPath = xPath;  
        this.columns = columns;  
    }  
   
    /** 
     * 取出列的数量 
     */  
    public int getColumnCount() throwsTableDataException {  
        return columns.length;  
    }  
   
    /** 
     * 取出相应的列的名称 
     */  
    public String getColumnName(intcolumnIndex) throws TableDataException { 
        if (columnIndex < 0 || columnIndex>= columns.length)  
            return null;  
        String columnName =columns[columnIndex] == null null  
                :columns[columnIndex].getName();  
   
        return columnName;  
    }  
   
    /** 
     * 取出得到的结果集的总的行数 
     */  
    public int getRowCount() throwsTableDataException {  
        this.init();  
        return row_list.size();  
    }  
   
    /** 
     * 取出相应位置的值 
     */  
    public Object getValueAt(int rowIndex, intcolumnIndex)  
            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() throwsTableDataException {  
        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(newFile(XMLParseDemoDataModel.this.filePath), 
                    new DemoHandler());  
        catch (Exception e) {  
            e.printStackTrace();  
           FRContext.getLogger().error(e.getMessage(), e);  
        
    }  
   
    /** 
     * 基本原理就是解析器在遍历文件时发现节点开始标记时,调用startElement方法读取节点内部内容时,调用characters方法 
     * 发现节点结束标记时,调用endElement 
     */  
    private class DemoHandler extendsDefaultHandler {  
        private List levelList = newArrayList(); // 记录当前节点的路径 
        private Object[] values; // 缓存一条记录  
        private int recordIndex = -1// 当前记录所对应的列的序号,-1表示不需要记录  
   
        public void startElement(String uri,String localName, String qName,  
                Attributes attributes) throwsSAXException {  
            // 记录下  
            levelList.add(qName);  
   
            if (isRecordWrapTag()) {  
                // 开始一条新数据的记录  
                values = newObject[XMLParseDemoDataModel.this.columns.length];  
            else if (needReadRecord()) {  
                // 看看其对应的列序号,下面的characters之后执行时,根据这个列序号来设置值存放的位置。  
                recordIndex =getColumnIndex(qName);  
            }  
        
   
        public void characters(char[] ch, intstart, 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 = newInteger(text);  
                else if (type.getType() ==COLUMN_TYPE_BOOLEAN) {  
                    value = newBoolean(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(StringcolumnName) {  
            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内字段名称是一一对用的。详细代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
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.javaXMLParseDemoDataModel.javaXMLDemoTableData.java三个类文件,将生成的class文件放于%FR_HOME%\WebReport\WEB-INF\classes\com\fr\data下。

 

配置程序数据源

新建报表,模板数据集>程序数据集,选择我们定义好的程序数据集XMLDemoTableData.class文件,名字可以自定义,如程序1

wKiom1f_R7SykfFwAAAor28fmhc521.png

6、使用程序数据源

在模板数据集窗口,点击预览按钮,弹出参数对话框,输入要显示的xml文件名称,点击确定则可以把Northwind.xml文件里面的数据读取出来转换报表数据源了,如下图:

wKiom1f_R9KjXkjoAACLLIQ8keA237.png



本文转自 雄霸天下啦 51CTO博客,原文链接:http://blog.51cto.com/10549520/1861568,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
用java实现云计算的两种趋势性方法
本文讲的是用java实现云计算的两种趋势性方法,最近,人们在思考如何使用Java来实现云计算,我看到了两种趋势性的方法。它们存在交叉重叠处而并非互相排斥,但总体而言它们在项目方面有着非常不同的表现
945 0
文章
问答
文章排行榜
最热
最新
相关电子书
更多
Java基础入门(四)-泛型、反射、注解
立即下载
JAVA反射原理以及一些常见的应用
立即下载
Java Spring Boot开发实战系列课程【第7讲】:Spring Boot 2.0安全机制与MVC身份验证实战(Java面试题)
立即下载