1. 程式人生 > >詳解Java解析XML的四種方法—DOM/SAX/jdom/dom4j

詳解Java解析XML的四種方法—DOM/SAX/jdom/dom4j

        最近在研究XML檔案的生成和解析,網上資料很多,當然也參差不齊。寫的沒錯誤的通常是單獨介紹了1種方法,介紹全的常常執行不起來。

        小哆把4種方法彙總了一下,執行驗證成功。

   xml解析

jar包免費下載:

    XML在不同的語言裡解析方式都是一樣的,只不過實現的語法不同而已。基本的解析方式有兩種,一種叫DOM,另一種叫SAX。SAX是基於事件流的解析,DOM是基於XML文件樹結構的解析。假設我們XML的內容和結構如下(demo.xml): 

<?xml version="1.0" encoding="UTF-8"?>
<employees>
	<!--An XML Note -->
	<?target text?>
	<employee id="lo" name="lele">
		<sex>m</sex>
		<age>23</age>
	</employee>
	<employee id="ve" name="fox">
		<sex>f</sex>
		<age>22</age>
	</employee>
</employees>

    1.DOM生成和解析XML文件

   W3C 規範化了 DOM,它的主要優點是可移植性:它是作為一種 CORBA 介面定義的,被對映到很多語言。因此如果瞭解了 JavaScript 中的 DOM,也就知道了 Java、C++、Perl、Python 和其他語言中的 DOM。

    解析器讀入整個文件,然後構建一個駐留記憶體的樹結構,然後程式碼就可以使用 DOM 介面來操作這個樹結構。優點:整個文件樹在記憶體中,便於操作;支援刪除、修改、重新排列等多種功能;缺點:將整個文件調入記憶體(包括無用的節點),浪費時間和空間;使用場合:一旦解析了文件還需多次訪問這些資料;硬體資源充足(記憶體、CPU)。


package cn.main.dom;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * DOM生成與解析XML文件
 * 
 * @author 莫小哆_ly 2012-2-20
 */
public class DomDemo {

    /*
     * 解析器讀入整個文件,然後構建一個駐留記憶體的樹結構,
     * 
     * 然後程式碼就可以使用 DOM 介面來操作這個樹結構。
     * 
     * 優點:整個文件樹在記憶體中,便於操作;支援刪除、修改、重新排列等多種功能;
     * 
     * 缺點:將整個文件調入記憶體(包括無用的節點),浪費時間和空間;
     * 
     * 使用場合:一旦解析了文件還需多次訪問這些資料;硬體資源充足(記憶體、CPU)
     */

    // 表示整個HTML或 XML文件。從概念上講,它是文件樹的根,並提供對文件資料的基本訪問
    private Document document;

    /**
     * 建立DOM樹
     * 
     * 要讀入一個XML文件,首先要一個DocumentBuilder物件
     */
    public void init() {
        // 獲取 DocumentBuilderFactory 的新例項
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        // 使用當前配置的引數建立一個新的 DocumentBuilder 例項
        DocumentBuilder builder = null;
        try {
            builder = factory.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        }
        // 獲取 DOM Document 物件的一個新例項來生成一個 DOM 樹
        this.document = builder.newDocument();
    }

    /**
     * xml文件的寫入操作
     * 
     * @param file
     */
    public void createXml(File file) {

        // 建立DOM樹
        this.init();

        // 建立XML根節點employees
        Element root = this.document.createElement("employees");
        // Adds the node newChild to the end of the list of children of this
        // node.
        // If the newChild is already in the tree, it is first removed.
        this.document.appendChild(root);

        // 1.建立根節點的子節點employee
        Element employee = this.document.createElement("employee");

        // 向根節點新增屬性節點
        Attr id = this.document.createAttribute("id");
        id.setNodeValue("0001");
        // 把屬性節點物件,追加到達employee節點;
        employee.setAttributeNode(id);

        // 宣告employee的子節點name
        Element name = this.document.createElement("name");
        // 向XML檔案name節點追加資料
        name.appendChild(this.document.createTextNode("wanglp"));
        // 把子節點的屬性追加到employee子節點中元素中
        employee.appendChild(name);

        // 宣告employee的子節點sex
        Element sex = this.document.createElement("sex");
        // 向XML檔案sex節點追加資料
        sex.appendChild(this.document.createTextNode("m"));
        // 把子節點的屬性追加到employee子節點中元素中
        employee.appendChild(sex);

        // 宣告employee的子節點age
        Element age = this.document.createElement("age");
        // 向XML檔案age節點追加資料
        age.appendChild(this.document.createTextNode("25"));
        // 把子節點的屬性追加到employee子節點中元素中
        employee.appendChild(age);

        // employee節點定義完成,追加到root
        root.appendChild(employee);

        // 2.建立根節點的子節點employee
        employee = this.document.createElement("employee");

        // 向根節點新增屬性節點
        id = this.document.createAttribute("id");
        id.setNodeValue("0002");
        // 把屬性節點物件,追加到達employee節點;
        employee.setAttributeNode(id);

        // 宣告employee的子節點name
        name = this.document.createElement("name");
        // 向XML檔案name節點追加資料
        name.appendChild(this.document.createTextNode("huli"));
        // 把子節點的屬性追加到employee子節點中元素中
        employee.appendChild(name);

        // 宣告employee的子節點sex
        sex = this.document.createElement("sex");
        // 向XML檔案sex節點追加資料
        sex.appendChild(this.document.createTextNode("f"));
        // 把子節點的屬性追加到employee子節點中元素中
        employee.appendChild(sex);

        // 宣告employee的子節點age
        age = this.document.createElement("age");
        // 向XML檔案age節點追加資料
        age.appendChild(this.document.createTextNode("12"));
        // 把子節點的屬性追加到employee子節點中元素中
        employee.appendChild(age);

        // employee節點定義完成,追加到root
        root.appendChild(employee);

        // 獲取 TransformerFactory 的新例項。
        TransformerFactory tf = TransformerFactory.newInstance();
        // 建立執行從 Source 到 Result 的複製的新 Transformer。能夠將源樹轉換為結果樹
        Transformer transformer = null;
        try {
            transformer = tf.newTransformer();
        } catch (TransformerConfigurationException e) {
            e.printStackTrace();
        }

        // 設定轉換中實際的輸出屬性
        // 指定首選的字元編碼
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        // indent="yes"|"no".指定了當輸出結果樹時,Transformer是否可以新增額外的空白
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        // 宣告檔案流
        PrintWriter pw = null;
        try {
            pw = new PrintWriter(new FileOutputStream(file));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            System.out.println("檔案沒有找到!");
        }
        // 充當轉換結果的持有者,可以為 XML、純文字、HTML 或某些其他格式的標記
        StreamResult result = new StreamResult(pw);
        // DOMSource implements Source
        DOMSource source = new DOMSource(document);

        try {
            // 將 XML Source 轉換為 Result
            transformer.transform(source, result);
        } catch (TransformerException e) {
            e.printStackTrace();
            System.out.println("生成XML檔案失敗!");
        }
        System.out.println("生成XML檔案成功!");
    }

    /**
     * xml文件的讀取操作
     * 
     * @param file
     */
    public void parserXml(File file) {
        // 獲取 DocumentBuilderFactory 的新例項
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        // 使用當前配置的引數建立一個新的 DocumentBuilder 例項
        DocumentBuilder builder;
        try {
            builder = factory.newDocumentBuilder();
            // 將給定 URI的內容解析為一個 XML文件,並且返回一個新的 DOM Document 物件
            document = builder.parse(file);
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 獲得文件根元素對物件;
        Element root = document.getDocumentElement();
        // 獲得文件根元素下一級子元素所有元素;
        NodeList nodeList = root.getChildNodes();

        System.out.print("<employees>");
        System.out.println(root.getNodeName());

        if (null != root) {
            for (int i = 0; i < nodeList.getLength(); i++) {
                Node child = nodeList.item(i);

                // 輸出child的屬性;
                System.out.print("<test>");
                System.out.println(child);

                if (child.getNodeType() == Node.ELEMENT_NODE) {
                    System.out.print("<id>");
                    System.out.println(child.getAttributes().getNamedItem("id").getNodeValue());
                }
                for (Node node = child.getFirstChild(); node != null; node = node.getNextSibling()) {
                    if (node.getNodeType() == Node.ELEMENT_NODE) {
                        if ("name".equals(node.getNodeName())) {
                            System.out.print("<name>");
                            System.out.println(node.getFirstChild().getNodeValue());
                        }
                    }
                    if (node.getNodeType() == Node.ELEMENT_NODE) {
                        if ("sex".equals(node.getNodeName())) {
                            System.out.print("<sex>");
                            System.out.println(node.getFirstChild().getNodeValue());
                        }
                    }
                    if (node.getNodeType() == Node.ELEMENT_NODE) {
                        if ("age".equals(node.getNodeName())) {
                            System.out.print("<age>");
                            System.out.println(node.getFirstChild().getNodeValue());
                        }
                    }
                    if (node.getNodeType() == Node.ELEMENT_NODE) {
                        if ("email".equals(node.getNodeName())) {
                            System.out.print("<email>");
                            System.out.println(node.getFirstChild().getNodeValue());
                        }
                    }
                }
            }
        }
        System.out.println("解析完畢");
    }

    /**
     * 測試
     */
    public static void main(String[] args) {

        // 為什麼有類似於這樣東西[#text:]
        // 原因是XML檔案元素之間的空白字元也是一個元素,<employees></employees>包含的空白
        DomDemo dom = new DomDemo();
        File file = new File("E://dom.xml");
        
        dom.createXml(file);
        dom.parserXml(file);
    }
}

2.SAX 解析XML文件

為解決DOM的問題,出現了SAX。SAX ,事件驅動。當解析器發現元素開始、元素結束、文字、文件的開始或結束等時,傳送事件,程式設計師編寫響應這些事件的程式碼,儲存資料。優點:不用事先調入整個文件,佔用資源少;SAX解析器程式碼比DOM解析器程式碼小,適於Applet,下載。缺點:不是持久的;事件過後,若沒儲存資料,那麼資料就丟了;無狀態性;從事件中只能得到文字,但不知該文字屬於哪個元素;使用場合:Applet;只需XML文件的少量內容,很少回頭訪問;機器記憶體少。

package cn.main.sax;

import java.io.File;

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;

/**
 * SAX解析XML文件
 * 
 * startDocument(),endDocument(),startElement(),endElement(),characters()
 * 
 * @author wanglp 2012-2-21
 */
public class SAXParseDemo extends DefaultHandler {

    private String tagValue; // 標籤值

    // 開始解析XML檔案
    public void startDocument() throws SAXException {
        System.out.println("開始解析");
    }

    // 結束解析XML檔案
    public void endDocument() throws SAXException {
        System.out.println("結束解析");
    }

    // 解析元素
    /**
     * 開始解析一個元素
     * @param qName 標籤名
     * @param attributes 屬性
     */
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes)
            throws SAXException {
        System.out.println(qName + "開始");
        // 屬性
        if (attributes != null && attributes.getLength() != 0) {
            System.out.println("屬性:");
            for (int i = 0; i < attributes.getLength(); i++) {
                System.out.print(attributes.getQName(i) + "="); // 屬性名
                System.out.print(attributes.getValue(i) + " "); // 屬性值
            }
            System.out.println();
        }
    }

    /**
     * 結束一個元素的解析 遇到結束標籤時呼叫此方法 通常在此方法對標籤取值並處理
     */
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        System.out.println(qName + "標籤值:" + tagValue);
        System.out.println(qName + "結束");
    }

    // 所有xml檔案中的字元都會放到ch[]中
    public void characters(char ch[], int start, int length) throws SAXException {
        tagValue = new String(ch, start, length).trim();
    }

    public static void main(String[] args) {
        File file = new File("src/cn/main/example/demo.xml");
        SAXParserFactory saxParFac = SAXParserFactory.newInstance();
        try {
            SAXParser saxParser = saxParFac.newSAXParser();
            saxParser.parse(file, new SAXParseDemo());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
3.JDOM生成和解析XML     要實現的功能簡單,如解析、建立等,但在底層,JDOM還是使用SAX(最常用)、DOM、Xanan文件。

    匯入jar包:jdom.jar

package cn.main.jdom;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

import org.jdom.Attribute;
import org.jdom.Comment;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

/**
 * 
 * jdom生成與解析XML文件
 *
 * @author wanglp 2012-2-23
 */
public class JdomDemo {

    Document document = new Document();

    /**
     * 利用JDom進行xml文件的寫入操作
     */
    public void createXml(File file) {

        // 1.建立元素 及 設定為根元素
        Element employees = new Element("employees");
        document.setContent(employees);

        // 2.建立註釋 及 設定到根元素上
        Comment commet = new Comment("this is my comment");
        employees.addContent(commet);

        // 3.建立元素
        Element element1 = new Element("employee");

        // 3.1 設定元素的屬性名及屬性值
        element1.setAttribute(new Attribute("id", "0001"));

        // 3.2 建立元素的屬性名及屬性值
        Attribute nameAttr = new Attribute("name", "wanglp");

        // 3.3 設定元素名及文字
        Element sexEle = new Element("sex");
        sexEle.setText("m");
        // 設定到上層元素上
        element1.addContent(sexEle);

        // 設定元素
        Element ageEle = new Element("age");
        ageEle.setText("22");
        element1.addContent(ageEle);

        // 設定為根元素的子元素
        employees.addContent(element1);
        // 將元素屬性設定到元素上
        element1.setAttribute(nameAttr);

        // 3.建立元素
        Element element2 = new Element("employee");

        // 3.1 設定元素的屬性名及屬性值
        element2.setAttribute(new Attribute("id", "0002"));

        // 3.2 建立元素的屬性名及屬性值
        Attribute name2Attr = new Attribute("name", "fox");

        // 3.3 設定元素名及文字
        Element sex2Ele = new Element("sex");
        sex2Ele.setText("f");
        // 設定到上層元素上
        element2.addContent(sex2Ele);

        // 設定元素
        Element age2Ele = new Element("age");
        age2Ele.setText("21");
        element2.addContent(age2Ele);

        // 設定為根元素的子元素
        employees.addContent(element2);
        // 將元素屬性設定到元素上
        element2.setAttribute(name2Attr);

        Element element3 = new Element("employee");
        element3.setText("title");
        element3.addContent(new Element("name").addContent(new Element("hello")));
        employees.addContent(element3);

        // 設定xml文件輸出的格式
        Format format = Format.getPrettyFormat();
        XMLOutputter out = new XMLOutputter(format);
        // 將得到的xml文件輸出到檔案流中
        try {
            out.output(document, new FileOutputStream(file));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 利用JDom進行xml文件的讀取操作
     */
    public void parserXml(File file) {
        // 建立解析器
        SAXBuilder builder = new SAXBuilder();
        try {
            // 將解析器與文件關聯
            document = builder.build(file);
        } catch (JDOMException e1) {
            e1.printStackTrace();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        // 讀取根元素
        Element root = document.getRootElement();
        // 輸出根元素的名字
        System.out.println("<" + root.getName() + ">");

        // 讀取元素集合
        List<?> employeeList = root.getChildren("employee");
        for (int i = 0; i < employeeList.size(); i++) {
            Element ele = (Element) employeeList.get(i);
            // 得到元素的名字
            System.out.println("<" + ele.getName() + ">");

            // 讀取元素的屬性集合
            List<?> empAttrList = ele.getAttributes();
            for (int j = 0; j < empAttrList.size(); j++) {
                Attribute attrs = (Attribute) empAttrList.get(j);
                // 將屬性的名字和值 並 輸出
                String name = attrs.getName();
                String value = (String) attrs.getValue();
                System.out.println(name + "=" + value);
            }
            try {
                Element sex = ele.getChild("sex");
                System.out.println("<sex>" + sex.getText());
                Element age = ele.getChild("age");
                System.out.println("<age>" + age.getText());
            } catch (NullPointerException e) {
                System.out.println(ele.getTextTrim());
                Element name = ele.getChild("name");
                System.out.println("<name>" + name.getName());
                
            }
            System.out.println("</employee>");
        }
        System.out.println("</employees>");
    }

    /**
     * 測試
     */
    public static void main(String[] args) {

        JdomDemo jdom = new JdomDemo();
        File file = new File("E://jdom.xml");
        jdom.createXml(file);
        jdom.parserXml(file);
    }
}
    4.DOM4J生成和解析XML文件
    DOM4J 是一個非常非常優秀的Java XML API,具有效能優異、功能強大和極端易用使用的特點,同時它也是一個開放原始碼的軟體。如今你可以看到越來越多的 Java 軟體都在使用 DOM4J 來讀寫 XML,特別值得一提的是連 Sun 的 JAXM 也在用 DOM4J。

    匯入jar包:dom4j-1.6.1.jar

package cn.main.dom4j;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

/**
 * 
 * dom4j生成與解析XML文件
 *
 * @author wanglp 2012-2-23
 */
public class Dom4jDemo {

    /**
     * 利用dom4j進行xml文件的寫入操作
     */
    public void createXml(File file) {

        // XML 宣告 <?xml version="1.0" encoding="UTF-8"?> 自動新增到 XML文件中

        // 使用DocumentHelper類建立文件例項(生成 XML文件節點的 dom4j API工廠類)
        Document document = DocumentHelper.createDocument();

        // 使用addElement()方法建立根元素 employees(用於向 XML 文件中增加元素)
        Element root = document.addElement("employees");

        // 在根元素中使用 addComment()方法添加註釋"An XML Note"
        root.addComment("An XML Note");

        // 在根元素中使用 addProcessingInstruction()方法增加一個處理指令
        root.addProcessingInstruction("target", "text");

        // 在根元素中使用 addElement()方法增加employee元素。
        Element empElem = root.addElement("employee");

        // 使用 addAttribute()方法向employee元素新增id和name屬性
        empElem.addAttribute("id", "0001");
        empElem.addAttribute("name", "wanglp");

        // 向employee元素中新增sex元素
        Element sexElem = empElem.addElement("sex");
        // 使用setText()方法設定sex元素的文字
        sexElem.setText("m");

        // 在employee元素中增加age元素 並設定該元素的文字。
        Element ageElem = empElem.addElement("age");
        ageElem.setText("25");

        // 在根元素中使用 addElement()方法增加employee元素。
        Element emp2Elem = root.addElement("employee");

        // 使用 addAttribute()方法向employee元素新增id和name屬性
        emp2Elem.addAttribute("id", "0002");
        emp2Elem.addAttribute("name", "fox");

        // 向employee元素中新增sex元素
        Element sex2Elem = emp2Elem.addElement("sex");
        // 使用setText()方法設定sex元素的文字
        sex2Elem.setText("f");

        // 在employee元素中增加age元素 並設定該元素的文字。
        Element age2Elem = emp2Elem.addElement("age");
        age2Elem.setText("24");

        // 可以使用 addDocType()方法新增文件型別說明。
        // document.addDocType("employees", null, "file://E:/Dtds/dom4j.dtd");
        // 這樣就向 XML 文件中增加文件型別說明:
        // <!DOCTYPE employees SYSTEM "file://E:/Dtds/dom4j.dtd">
        // 如果文件要使用文件型別定義(DTD)文件驗證則必須有 Doctype。

        try {
            XMLWriter output = new XMLWriter(new FileWriter(file));
            output.write(document);
            output.close();
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }

    /**
     * 利用dom4j進行xml文件的讀取操作
     */
    public void parserXml(File file) {

        Document document = null;

        // 使用 SAXReader 解析 XML 文件 catalog.xml:
        SAXReader saxReader = new SAXReader();

        try {
            document = saxReader.read(file);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        // 將字串轉為XML
        // document = DocumentHelper.parseText(fileString);

        // 獲取根節點
        Element root = document.getRootElement();
        // 列印節點名稱
        System.out.println("<" + root.getName() + ">");

        // 獲取根節點下的子節點遍歷
        Iterator<?> iter = root.elementIterator("employee");
        // 遍歷employee節點
        while (iter.hasNext()) {
            // 獲取當前子節點
            Element empEle = (Element) iter.next();
            System.out.println("<" + empEle.getName() + ">");

            // 獲取當前子節點的屬性遍歷
            Iterator<?> attrList = empEle.attributeIterator();
            while (attrList.hasNext()) {
                Attribute attr = (Attribute) attrList.next();
                System.out.println(attr.getName() + "=" + attr.getValue());
            }

            // 遍歷employee節點下所有子節點
            Iterator<?> eleIte = empEle.elementIterator();
            while (eleIte.hasNext()) {
                Element ele = (Element) eleIte.next();
                System.out.println("<" + ele.getName() + ">" + ele.getTextTrim());
            }

            // 獲取employee節點下的子節點sex值
            // String sex = empEle.elementTextTrim("sex");
            // System.out.println("sex:" + sex);

        }
        System.out.println("</" + root.getName() + ">");
    }

    public static void main(String[] args) {

        Dom4jDemo dom4j = new Dom4jDemo();
        File file = new File("e:/dom4j.xml");
        // dom4j.createXml(file);

        dom4j.parserXml(file);

    }
}