Java如何解析某個目錄下xml文件,將XML文件轉換爲報表數據源?

在Java開發的報表工具FineReport中,假如在目錄下保存了幾個XML文件,但願把XML文件轉換爲報表數據源,同時但願展現動態xml數據源的效果,這時可經過參數的方式,動態獲取xml字段中的值再做爲報表數據源。 Northwind.xml記錄數據格式以下:java

<?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字段數據進行解析,最終返回所但願的數據表。實現步驟以下: 一、 定義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;  
    }  
}

二、定義XMLParseDemoDataModel.java類文件 定義XMLParseDemoDataModel.java類繼承AbstractDataModel接口,實現getColumnCount、getColumnName、getRowCount、getValueAt四個方法,詳細代碼以下:ide

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;  
        }  
    }  
}

三、定義程序數據集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裏面某個標籤名。 四、編譯程序數據源 分別編譯XMLColumnNameType4Demo.java、XMLParseDemoDataModel.java、XMLDemoTableData.java三個類文件,將生成的class文件放於%FR_HOME%\WebReport\WEB-INF\classes\com\fr\data下。工具

5 配置程序數據源 新建報表,模板數據集>程序數據集,選擇咱們定義好的程序數據集XMLDemoTableData.class文件,名字能夠自定義,如程序1。this

六、使用程序數據源 在模板數據集窗口,點擊預覽按鈕,彈出參數對話框,輸入要顯示的xml文件名稱,點擊肯定則能夠把Northwind.xml文件裏面的數據讀取出來轉換報表數據源了,以下圖:code

相關文章
相關標籤/搜索