Java文件操作①——XML文件的讀取

一、邂逅XML

文件種類是豐富多彩的,XML作爲衆多文件類型的一種,經常被用於數據存儲和傳輸。所以XML在現今應用程序中是非常流行的。本文主要講Java解析和生成XML。用於不同平臺、不同設備間的數據共享通信。

XML文件的表現:以“.xml”爲文件擴展名的文件;

   存儲結構:樹形結構;

 

節點名稱區分大小寫。

1、<book id="1"></book> id爲屬性, <book><id>1</id></book> id爲節點
2、xml文件開頭要加上版本信息和編碼方式<?xml version="1.0" encoding="UTF-8"?>

 比如:

❤ 爲什麼要使用XML?

思考1:不同應用程序之間的通信?

思考2:不同平臺間的通信?

思考3:不同平臺間的數據共享?

答案就是我們要學習的XML文件。我們可以使用相同的xml把不同的文件聯繫起來

二、應用 DOM 方式解析 XML

❤ 在Java程序中如何獲取XML文件的內容

解析的目的:獲取節點名、節點值、屬性名、屬性值;

四種解析方式:DOM、SAX、DOM4J、JDOM 

DOM、SAX :java 官方方式,不需要下載jar包
DOM4J、JDOM :第三方,需要網上下載jar包

示例:解析XML文件,目標是解析XML文件後,Java程序能夠得到xml文件的所有數據

思考:如何在Java程序中保留xml數據的結構?

 

如何保留節點之間的層級關係?

注意常用的節點類型:

 下面介紹DOM方式解析XML:

 功能說明:

代碼示例:

package com.study.domtest;

import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

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

/**
 * DOM方式解析xml
 */
public class DOMTest {

    public static void main(String[] args) {
        //1、創建一個DocumentBuilderFactory的對象
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        //2、創建一個DocumentBuilder的對象
        try {
            //創建DocumentBuilder對象
            DocumentBuilder db = dbf.newDocumentBuilder();
            //3、通過DocumentBuilder對象的parser方法加載books.xml文件到當前項目下
            /*注意導入Document對象時,要導入org.w3c.dom.Document包下的*/
            Document document = db.parse("books.xml");//傳入文件名可以是相對路徑也可以是絕對路徑
            //獲取所有book節點的集合
            NodeList bookList = document.getElementsByTagName("book");
            //通過nodelist的getLength()方法可以獲取bookList的長度
            System.out.println("一共有" + bookList.getLength() + "本書");
            //遍歷每一個book節點
            for (int i = 0; i < bookList.getLength(); i++) {
                System.out.println("=================下面開始遍歷第" + (i + 1) + "本書的內容=================");
        //❤未知節點屬性的個數和屬性名時:
                //通過 item(i)方法 獲取一個book節點,nodelist的索引值從0開始
                Node book = bookList.item(i);
                //獲取book節點的所有屬性集合
                NamedNodeMap attrs = book.getAttributes();
                System.out.println("第 " + (i + 1) + "本書共有" + attrs.getLength() + "個屬性");
                //遍歷book的屬性
                for (int j = 0; j < attrs.getLength(); j++) {
                    //通過item(index)方法獲取book節點的某一個屬性
                    Node attr = attrs.item(j);
                    //獲取屬性名
                    System.out.print("屬性名:" + attr.getNodeName());
                    //獲取屬性值
                    System.out.println("--屬性值" + attr.getNodeValue());
                }
          //❤已知book節點有且只有1個id屬性:
             /* 
              //前提:已經知道book節點有且只能有1個id屬性
              //將book節點進行強制類型轉換,轉換成Element類型
              Element book1 = (Element) bookList.item(i);
              //通過getAttribute("id")方法獲取屬性值
              String attrValue = book1.getAttribute("id");
              System.out.println("id屬性的屬性值爲" + attrValue);
              */
                
             //解析book節點的子節點
                NodeList childNodes = book.getChildNodes();
              //遍歷childNodes獲取每個節點的節點名和節點值
                System.out.println("第" + (i+1) + "本書共有" + childNodes.getLength() + "個子節點");
                for (int k = 0; k < childNodes.getLength(); k++) {
                  //區分出text類型的node以及element類型的node
                    if(childNodes.item(k).getNodeType() == Node.ELEMENT_NODE){
                      //獲取了element類型節點的節點名
                        System.out.print("第" + (k + 1) + "個節點的節點名:" + childNodes.item(k).getNodeName());
                      //獲取了element類型節點的節點值
                        System.out.println("--節點值是:" + childNodes.item(k).getFirstChild().getNodeValue());
//                        System.out.println("--節點值是:" + childNodes.item(k).getTextContent());
                    }
                }
                System.out.println("======================結束遍歷第" + (i + 1) + "本書的內容=================");
            }

        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}


三、應用 SAX 方式解析 XML

SAX是SIMPLE API FOR XML的縮寫,與DOM比較而言,SAX是一種輕量型的方法。

Dom解析會將整個xml文件加載到內存中,然後再逐個解析
Sax解析是通過Handler處理類逐個依次解析每個節點

在處理DOM的時候,我們需要讀入整個的XML文檔,然後在內存中創建DOM樹,生成DOM樹上的每個NODE對象。當文檔比較小的時候,這不會造成什麼問題,但是一旦文檔大起來,處理DOM就會變得相當費時費力。特別是其對於內存的需求,也將是成倍的增長,以至於在某些應用中使用DOM是一件很不划算的事。這時候,一個較好的替代解決方法就是SAX。 SAX在概念上與DOM完全不同。首先,不同於DOM的文檔驅動,它是事件驅動的,也就是說,它並不需要讀入整個文檔,而文檔的讀入過程也就是SAX的解析過程。所謂事件驅動,是指一種基於回調(callback)機制的程序運行方法。在XMLReader接受XML文檔,在讀入XML文檔的過程中就進行解析,也就是說讀入文檔的過程和解析的過程是同時進行的,這和DOM區別很大。

❤ 

代碼示例:Book實體類

package com.study.saxtest.entity;

/**
 * 用Book實體類代表XML文件中的"<book>...</book>"中整個元素
 * 在遇到<book>標籤,證明我們要存儲新的book時需要創建Book對象
 */
public class Book {
    private String id;
    private String name;
    private String author;
    private String year;
    private String price;
    private String language;
    public String getId() {
     return id;
    }
    public void setId(String id) {
     this.id = id;
    }
    public String getName() {
     return name;
    }
    public void setName(String name) {
     this.name = name;
    }
    public String getAuthor() {
     return author;
    }
    public void setAuthor(String author) {
     this.author = author;
    }
    public String getYear() {
     return year;
    }
    public void setYear(String year) {
     this.year = year;
    }
    public String getPrice() {
     return price;
    }
    public void setPrice(String price) {
     this.price = price;
    }
    public String getLanguage() {
     return language;
    }
    public void setLanguage(String language) {
     this.language = language;
    }
}


SAXParserHandler類:

package com.study.saxtest.handler;

import java.util.ArrayList;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.study.saxtest.entity.Book;

public class SAXParserHandler extends DefaultHandler{
    /*注意DefaultHandler是org.xml.sax.helpers包下的*/
    
    int bookIndex = 0;//設置全局變量,用來記錄是第幾本書
    
    String value = null;
    Book book = null;
    private ArrayList<Book> bookList = new ArrayList<Book>();//保存book對象

    public ArrayList<Book> getBookList() {
        return bookList;
    }

    /**
     * 用來標識解析開始
     */
    @Override
    public void startDocument() throws SAXException {
        super.startDocument();
        System.out.println("SAX解析開始");

    }
    
    /**
     * 用來標識解析結束
     */
    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
        System.out.println("SAX解析結束");
    }
    
    /**
     * 用來遍歷xml文件的開始標籤
     * 解析xml元素
     */
    @Override
    public void startElement(String uri, String localName, String qName,Attributes attributes) throws SAXException {
        //調用DefaultHandler類的startElement方法
        super.startElement(uri, localName, qName, attributes);
        if (qName.equals("book")) {
            bookIndex++;
            //創建一個book對象
            /*Book*/ book = new Book();
            //開始解析book元素的屬性
            System.out.println("======================開始遍歷第"+bookIndex+"本書的內容=================");
           /* //❤已知節點的屬性名時:比如已知id屬性,根據屬性名稱獲取屬性值
            String value = attributes.getValue("id");
            System.out.print("book的屬性值是:"+value);*/
          //❤未知節點的屬性名時,獲取屬性名和屬性值
            int num=attributes.getLength();
            for(int i=0;i<num;i++){
                System.out.print("book元素的第"+(i+1)+"個屬性名是:"+attributes.getQName(i));
                System.out.println("---屬性值是:"+attributes.getValue(i));
                if (attributes.getQName(i).equals("id")) {//往book對象中塞值
                    book.setId(attributes.getValue(i));
                }
            }
        }else if (!qName.equals("book") && !qName.equals("bookstore")) {
            System.out.print("節點名是:" + qName + "---");//此時qName獲取的是節點名(標籤)
        }
    }
    
    /**
     * 用來遍歷xml文件的結束標籤
     */
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        //調用DefaultHandler類的endElement方法
        super.endElement(uri, localName, qName);
        //判斷是否針對一本書已經遍歷結束
        if (qName.equals("book")) {
            bookList.add(book);//在清空book對象之前先保存
            book = null;//把book清空,方便解析下一個book節點
            System.out.println("======================結束遍歷第"+bookIndex+"本書的內容=================");
        }else if (qName.equals("name")) {
            book.setName(value);
        }
        else if (qName.equals("author")) {
         book.setAuthor(value);
        }
        else if (qName.equals("year")) {
         book.setYear(value);
        }
        else if (qName.equals("price")) {
         book.setPrice(value);
        }
        else if (qName.equals("language")) {
         book.setLanguage(value);
        }
    }
    
    /**
     * 獲取文本
     * 重寫charaters()方法時,
     * String(byte[] bytes,int offset,int length)的構造方法進行數組的傳遞
     * 去除解析時多餘空格
     */
    @Override
    public void characters(char[] ch, int start, int length)throws SAXException {
        /**
         * ch 代表節點中的所有內容,即每次遇到一個標籤調用characters方法時,數組ch實際都是整個XML文檔的內容
         * 如何每次去調用characters方法時我們都可以獲取不同的節點屬性?這時就必須結合start(開始節點)和length(長度)
         */
        super.characters(ch, start, length);
        /*String */value = new String(ch, start, length);//value獲取的是文本(開始和結束標籤之間的文本)
//        System.out.println(value);//輸出時會多出兩個空格,是因爲xml文件中空格與換行字符被看成爲一個文本節點
        if(!value.trim().equals("")){//如果value去掉空格後不是空字符串
            System.out.println("節點值是:" + value);
        }
    }
    
    /**
     * qName獲取的是節點名(標籤)
     * value獲取的是文本(開始和結束標籤之間的文本)
     * 思考:qName和value分別在兩個方法中,如何將這兩個方法中的參數整合到一起?
     * 分析:要在兩個方法中用同一個變量,就設置成全局變量,可以賦初值爲null。
     *     可以把characters()方法中的value作成一個全局變量
     * 
     * 然後在endElement()方法中對book對象進行塞值。記得要把Book對象設置爲全局變量,變量共享
     */
}


測試類:SAXTest

package com.study.saxtest.test;

import java.io.IOException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.SAXException;

import com.study.saxtest.entity.Book;
import com.study.saxtest.handler.SAXParserHandler;

/**
 * sax方式解析XML
 */
public class SAXTest {

    public static void main(String[] args) {
        //1.獲取一個SAXParserFactory的實例對象
        SAXParserFactory factory = SAXParserFactory.newInstance();
        //2.通過factory的newSAXParser()方法獲取一個SAXParser類的對象。
        try {
            SAXParser parser = factory.newSAXParser();
            //創建SAXParserHandler對象
            SAXParserHandler handler = new SAXParserHandler();
            parser.parse("books.xml", handler);
            System.out.println("~~~~~共有"+handler.getBookList().size()+"本書");
            for (Book book : handler.getBookList()) {
                System.out.println(book.getId());
                System.out.println(book.getName());
                System.out.println(book.getAuthor());
                System.out.println(book.getYear());
                System.out.println(book.getPrice());
                System.out.println(book.getLanguage());
                System.out.println("----finish----");
            }
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}


 運行結果:

SAX解析開始
======================開始遍歷第1本書的內容=================
book元素的第1個屬性名是:id---屬性值是:1
節點名是:name---節點值是:冰與火之歌
節點名是:author---節點值是:喬治馬丁
節點名是:year---節點值是:2014
節點名是:price---節點值是:89
======================結束遍歷第1本書的內容=================
======================開始遍歷第2本書的內容=================
book元素的第1個屬性名是:id---屬性值是:2
節點名是:name---節點值是:安徒生童話
節點名是:year---節點值是:2004
節點名是:price---節點值是:77
節點名是:language---節點值是:English
======================結束遍歷第2本書的內容=================
SAX解析結束
~~~~~共有2本書
冰與火之歌
喬治馬丁
89
null
----finish----
安徒生童話
null
77
English
----finish----

四、應用 DOM4J 及 JDOM 方式解析 XML

 # JDOM 方式解析 XML

  JDOM 開始解析前的準備工作:

  JDOM是第三方提供的解析XML方法,需要jdom-2.0.5.jar包

 

 示例代碼:

package com.study.jdomtest1.test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

import com.study.jdomtest1.entity.Book;

/**
 *  JDOM 解析XML
 */
public class JDOMTest {
    private static ArrayList<Book> booksList = new ArrayList<Book>();

    public static void main(String[] args) {
        // 進行對books.xml文件的JDOM解析
        //❤準備工作
        // 1.創建一個SAXBuilder的對象
        SAXBuilder saxBuilder = new SAXBuilder();//注意SAXBuilder是org.jdom2.input包下的
        InputStream in;
        try {
            // 2.創建一個輸入流,將xml文件加載到輸入流中
            in=new FileInputStream("books.xml");//如果將xml文件放在src/res包下,此時應該輸入“src/res/books.xml”
            InputStreamReader isr = new InputStreamReader(in, "UTF-8");//使用包裝流InputStreamReader進行讀取編碼的指定,防止亂碼
            // 3.通過saxBuilder的build方法,將輸入流加載到saxBuilder中
            Document document = saxBuilder.build(isr);
            // 4.通過document對象獲取xml文件的根節點
            Element rootElement = document.getRootElement();
            // 5.獲取根節點下的子節點的List集合
            List<Element> bookList = rootElement.getChildren();
            //❤ 繼續解析,採用for循環對bookList進行遍歷
            for (Element book : bookList) {
                Book bookEntity = new Book();
                System.out.println("======開始解析第" + (bookList.indexOf(book) + 1) + "書======");//indexOf()返回的是index的位置,是從0開始
                // 解析book的屬性集合
                List<Attribute> attrList = book.getAttributes();//適用於未知屬性情況下
                /*//知道節點下屬性名稱時,獲取節點值
                book.getAttributeValue("id");*/
                // 遍歷attrList(針對不清楚book節點下屬性的名字及數量)
                for (Attribute attr : attrList) {
                    /**注:JDom中,Attribute的getName和getValue方法獲取到的都是實際的名稱和值,
                     * 不會出現SAX和DOM中的空格和換行的情況*/
                    // 獲取屬性名
                    String attrName = attr.getName();
                    // 獲取屬性值
                    String attrValue = attr.getValue();
                    System.out.println("屬性名:" + attrName + "----屬性值:" + attrValue);
                    if (attrName.equals("id")) {
                        bookEntity.setId(attrValue);
                       }
                }
                
                //❤對book節點的子節點的節點名以及節點值的遍歷
                List<Element> bookChilds = book.getChildren();
                for (Element child : bookChilds) {
                    System.out.println("節點名:" + child.getName() + "----節點值:" + child.getValue());
                    if (child.getName().equals("name")) {
                        bookEntity.setName(child.getValue());
                       }
                       else if (child.getName().equals("author")) {
                        bookEntity.setAuthor(child.getValue());
                       }
                       else if (child.getName().equals("year")) {
                        bookEntity.setYear(child.getValue());
                       }
                       else if (child.getName().equals("price")) {
                        bookEntity.setPrice(child.getValue());
                       }
                       else if (child.getName().equals("language")) {
                        bookEntity.setLanguage(child.getValue());
                       }

                }
                System.out.println("======結束解析第" + (bookList.indexOf(book) + 1) + "書======");
                booksList.add(bookEntity);
                bookEntity = null;
                //測試數據
                System.out.println(booksList.size());
                System.out.println(booksList.get(0).getId());
                System.out.println(booksList.get(0).getName());

            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}


注意:

❤關於 JDOM 使用過程中 JAR 包的引用 :

方式1:通過右擊項目-->build path-->add external archives...-->然後選擇本地文件的jar包
這種方式並不能將jar包真正導入到項目源碼中,當把項目導出放在另外的機器上,這個jar包並不會隨着project一同被導出。

如圖:

方式2:項目根目錄下新建lib文件夾——複製、粘帖jar包——右擊jar包選擇“build path構建路徑”——“add to build path添加至構建路徑”即可

如圖:

  # DOM4J 方式解析 XML 

  DOM4J 是第三方提供的解析XML方法,需要dom4j-1.6.1.jar包

示例:

package com.study.dom4jtest;

import java.io.File;
import java.util.Iterator;
import java.util.List;

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

/**
 * DOM4J 方式解析XML
 */
public class DOM4JTest {

    public static void main(String[] args) {
        // 解析books.xml文件
        // 創建SAXReader的對象reader
        SAXReader reader = new SAXReader();
        try {
            // 通過reader對象的read方法加載books.xml文件,獲取docuemnt對象。
            Document document = reader.read(new File("books.xml"));
            // 通過document對象獲取根節點bookstore
            Element bookStore = document.getRootElement();
            // 通過element對象的elementIterator方法獲取迭代器
            Iterator it = bookStore.elementIterator();
            // 遍歷迭代器,獲取根節點中的信息(書籍)
            while (it.hasNext()) {
             System.out.println("=====開始遍歷某一本書=====");
             Element book = (Element) it.next();
             // 獲取book的屬性名以及 屬性值
             List<Attribute> bookAttrs = book.attributes();
             for (Attribute attr : bookAttrs) {
              System.out.println("屬性名:" + attr.getName() + "--屬性值:" + attr.getValue());
             }
             //解析子節點的信息
             Iterator itt = book.elementIterator();
             while (itt.hasNext()) {
              Element bookChild = (Element) itt.next();
              System.out.println("節點名:" + bookChild.getName() + "--節點值:" + bookChild.getStringValue());
             }
             System.out.println("=====結束遍歷某一本書=====");
            }

        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

}


五、四種解析方式比較分析

基礎方法:DOM(平臺無關的官方解析方式)、SAX(基於事件驅動的解析方式)
擴展方法:JDOM、DOM4J(在基礎的方法上擴展出的,只有在java中能夠使用的解析方法)

##解析速度的分析 

 

XML四種解析方式性能測試:
SAX>DOM>DOM4J>JDOM

JUnit是Java提供的一種進行單元測試的自動化工具。測試方法可以寫在任意類中的任意位置。使用JUnit可以沒有main()入口進行測試。
DOM4J在靈活性和對複雜xml的支持上都要強於DOM
DOM4J的應用範圍非常的廣,例如在三大框架的Hibernate中是使用DOM4J的方式解析文件的。
DOM是w3c組織提供的一個官方解析方式,在一定程度上是有所應用的。
當XML文件比較大的時候,會發現DOM4J比較好用
1.JUnit:Java提供的單元測試;@Test註解;採用JUnit不需要程序入口main方法
2.性能測試結果:幾kB的xml文件;建議使用DOM4J解析
  DOM-33ms
  SAX-6ms
  JDOM-69ms
  DOM4J-45ms
工程右鍵build path --Add library--JUnit單元測試 --version:JUnit4
  DOM:33,SAX:6
  JDOM:69;DOM4J:45
  DOM 有可能溢出
多使用DOM4J

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章