1. 程式人生 > >Java高級特性 第14節 解析XML文檔(2) - SAX 技術

Java高級特性 第14節 解析XML文檔(2) - SAX 技術

rim ride brush books 適合 函數實現 jdk 特性 自定義類

一、SAX解析XML文檔

  SAX的全稱是Simple APIs for XML,也即XML簡單應用程序接口。與DOM不同,SAX提供的訪問模式是一種順序模式,這是一種快速讀寫XML數據的方式。當使用SAX分析器對XML文檔進行分析時,會觸發一系列事件,並激活相應的事件處理函數,應用程序通過這些事件處理函數實現對XML文檔的訪問,因而SAX接口也被稱作事件驅動接口

  1. SAX解析原理

  加載一點,讀取一點,處理一點。對內存要求比較低。

  SAX解析工具內置在jdk中:org.xml.sax.*

  技術分享圖片

  2. SAX解析工具核心:
  核心的API:

  • SAXParser類:
    用於讀取和解析xml文件對象
  • parse(File f, DefaultHandler dh)方法: 解析xml文件
    • 參數一: File:表示 讀取的xml文件
    • 參數二: DefaultHandler: SAX事件處理程序。使用DefaultHandler的子類

  3. 解析步驟:

  • 第一步:創建對象

  1)創建SAXParser對象

SAXParser parser = SAXParserFactory.newInstance().newSAXParser();

  2)調用parse方法

/**
 * 參數一: xml文檔  * 參數二: DefaultHandler的子類 MyDefaultHandler()為自定義  */ parser.parse(new File(".\\src\\Go\\person.xml"), new MyDefaultHandler());

  註意: 這裏創建SAXParser對象 不能直接通過構造函數來創造,因為用到了單例工廠模式。

  DefaultHandler類的API:後三個最重要

    • void startDocument() : 在讀到文檔開始時調用void endDocument() :在讀到文檔結束時調用
    • void startElement(String uri, String localName, String qName, Attributes attributes) : 讀到開始標簽時調用
    • void endElement(String uri, String localName, String qName) :讀到結束標簽時調用
    • void characters(char[] ch, int start, int length) : 讀到文本內容時調用
  • 第二步:自定義類繼承DefaultHandler重寫方法

  這些都是要重寫的,舉個例子:

public class MyDefaultHandler extends DefaultHandler {

  /**
  * 開始文檔時調用
  */
  @Override
  public void startDocument() throws SAXException {
    System.out.println("MyDefaultHandler.startDocument()");
  }

  /**
  * 開始標簽時調用
  * @param qName: 表示開始標簽的標簽名
  * @param attributes: 表示開始標簽內包含的屬性列表
  */
  @Override
  public void startElement(String uri, String localName, String qName,Attributes attributes) throws SAXException {
    System.out.println("MyDefaultHandler.startElement()-->"+qName);
  }

  /**
  * 結束標簽時調用
  * @param qName: 結束標簽的標簽名稱
  */
  @Override
  public void endElement(String uri, String localName, String qName) throws SAXException {
    System.out.println("MyDefaultHandler.endElement()-->"+qName);
  }

  /**
  * 讀到文本內容的時調用
  * @param ch: 表示當前讀完的所有文本內容
  * @param start: 表示當前文本內容的開始位置
  * @param length: 表示當前文本內容的長度
  * char[]( 張三 20) 100
  * 98 2 
  */ 
  @Override
  public void characters(char[] ch, int start, int length) throws SAXException {
    //得到當前文本內容
    String content = new String(ch,start,length);
    System.out.println("MyDefaultHandler.characters()-->"+content);
  }

  /**
  * 結束文檔時調用
  */
  @Override
  public void endDocument() throws SAXException {
    System.out.println("MyDefaultHandler.endDocument()");
  }
}

  xml文件: 

<?xml version="1.0" encoding="utf-8"?>
<contactList>
  <contact id="001" name="eric">
    <name>張三</name>
    <age>20</age>
    <phone>134222223333</phone>
    <email>[email protected]</email>
    <qq>432221111</qq>
  </contact>
  <contact id="002" name="jacky">
    <name>eric</name>
    <age>20</age>
    <phone>134222225555</phone>
    <email>[email protected]</email>
    <qq>432222222</qq>
  </contact>
</contactList>

  結果:

MyDefaultHandler.startDocument()
MyDefaultHandler.startElement()-->contactList
MyDefaultHandler.characters()-->

MyDefaultHandler.startElement()-->contact
MyDefaultHandler.characters()-->

MyDefaultHandler.startElement()-->name
MyDefaultHandler.characters()-->張三
MyDefaultHandler.endElement()-->name
MyDefaultHandler.characters()-->

MyDefaultHandler.startElement()-->age
MyDefaultHandler.characters()-->20
MyDefaultHandler.endElement()-->age
MyDefaultHandler.characters()-->

MyDefaultHandler.startElement()-->phone
MyDefaultHandler.characters()-->134222223333
MyDefaultHandler.endElement()-->phone
MyDefaultHandler.characters()-->

MyDefaultHandler.startElement()-->email
MyDefaultHandler.characters()-->[email protected]
MyDefaultHandler.endElement()-->email
MyDefaultHandler.characters()-->

MyDefaultHandler.startElement()-->qq
MyDefaultHandler.characters()-->432221111
MyDefaultHandler.endElement()-->qq
MyDefaultHandler.characters()-->

MyDefaultHandler.endElement()-->contact
MyDefaultHandler.characters()-->

MyDefaultHandler.startElement()-->contact
MyDefaultHandler.characters()-->

MyDefaultHandler.startElement()-->name
MyDefaultHandler.characters()-->eric
MyDefaultHandler.endElement()-->name
MyDefaultHandler.characters()-->

MyDefaultHandler.startElement()-->age
MyDefaultHandler.characters()-->20
MyDefaultHandler.endElement()-->age
MyDefaultHandler.characters()-->

MyDefaultHandler.startElement()-->phone
MyDefaultHandler.characters()-->134222225555
MyDefaultHandler.endElement()-->phone
MyDefaultHandler.characters()-->

MyDefaultHandler.startElement()-->email
MyDefaultHandler.characters()-->[email protected]
MyDefaultHandler.endElement()-->email
MyDefaultHandler.characters()-->

MyDefaultHandler.startElement()-->qq
MyDefaultHandler.characters()-->432222222
MyDefaultHandler.endElement()-->qq
MyDefaultHandler.characters()-->

MyDefaultHandler.endElement()-->contact
MyDefaultHandler.characters()-->

MyDefaultHandler.endElement()-->contactList
MyDefaultHandler.endDocument()

  註意:結果中出現MyDefaultHandler.characters()–>空白 ,是因為<contactList> <contact>之間也是有文本的,是換行和空格被characters方法讀取了。

二、DOM解析和SAX解析總結

DOM解析 SAX解析
原理: 一次性加載xml文檔,不適合大容量的文件讀取 原理: 加載一點,讀取一點,處理一點。適合大容量文件的讀取
DOM解析可以任意進行增刪改成 SAX解析只能讀
DOM解析任意讀取任何位置的數據,甚至往回讀 取SAX解析只能從上往下,按順序讀取,不能往回讀
DOM解析面向對象的編程方法(Node,Element,Attribute),Java開發者編碼比較簡單。 SAX解析基於事件的編程方法。java開發編碼相對復雜

三、使用SAX讀取XML數據實例

  books.xml:

<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
    <book id="1">
        <name>冰與火之歌</name>
        <author>喬治馬丁</author>
        <year>2014</year>
        <price>89</price>
    </book>
    <book id="2">
        <name>安徒生童話</name>
        <year>2004</year>
        <price>77</price>
        <language>English</language>
    </book>    
</bookstore>

  SAXParserHandler.java:

public class SAXParserHandler extends DefaultHandler {
    String value = null;
    Book book = null;
    private ArrayList<Book> bookList = new ArrayList<Book>();
    public ArrayList<Book> getBookList() {
        return bookList;
    }

    int bookIndex = 0;
    /**
     * 用來標識解析開始
     */
    @Override
    public void startDocument() throws SAXException {
        // TODO Auto-generated method stub
        super.startDocument();
        System.out.println("SAX解析開始");
    }
    
    /**
     * 用來標識解析結束
     */
    @Override
    public void endDocument() throws SAXException {
        // TODO Auto-generated method stub
        super.endDocument();
        System.out.println("SAX解析結束");
    }
    
    /**
     * 解析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 = new Book();
            //開始解析book元素的屬性
            System.out.println("======================開始遍歷某一本書的內容=================");
            //不知道book元素下屬性的名稱以及個數,如何獲取屬性名以及屬性值
            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.setId(attributes.getValue(i));
                }
            }
        }
        else if (!qName.equals("name") && !qName.equals("bookstore")) {
            System.out.print("節點名是:" + qName + "---");
        }
    }
    
    @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 = null;
            System.out.println("======================結束遍歷某一本書的內容=================");
        }
        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);
        }
    }
    
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        // TODO Auto-generated method stub
        super.characters(ch, start, length);
        value = new String(ch, start, length);
        if (!value.trim().equals("")) {
            System.out.println("節點值是:" + value);
        }
    }
}

  SAXTest.java:

public class SAXTest {
    /**
     * @param args
     */
    public static void main(String[] args) {
        //錕斤拷取一錕斤拷SAXParserFactory錕斤拷實錕斤拷
        SAXParserFactory factory = SAXParserFactory.newInstance();
        //通錕斤拷factory錕斤拷取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) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SAXException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

  

Java高級特性 第14節 解析XML文檔(2) - SAX 技術