天天看點

JavaEE實戰——XML文檔DOM、SAX、STAX解析方式詳解前言簡介JAXP DOM 解析JAXP SAX 解析XML PULL 解析綜合案例

前言

    本文接着上一篇部落格進行XML文檔解析處理文法的介紹。在上一篇部落格XML文法中我們提到了,XML技術在企業中主要應用在存儲、傳輸資料和作為架構的配置檔案這兩塊領域。本部落格介紹的技術主要就是應用在通過XML進行存儲和傳輸資料這一塊。大緻分為:JAXP DOM 解析、JAXP SAX 解析、XML PULL 進行 STAX 解析這三個方面。

簡介

使用xml 存儲和傳輸資料

1、通過程式生成xml

2、讀取xml中資料 ---- xml 解析

XML解析方式有三種:DOM、SAX、StAX

三種解析方式對應着三種解析思想,表述如下。

什麼是DOM、SAX、StAX ?

DOM Document Object Model ----- 文檔對象模型 

DOM思想: 将整個xml 加載記憶體中,形成文檔對象,所有對xml操作都對記憶體中文檔對象進行

DOM 是官方xml解析标準

* 是以DOM是所有開發語言都支援的 ---- Java、JavaScript 都支援DOM

SAX Simple API for XML ----- XML 簡單 API 

程式員為什麼發明sax解析方式?? 當xml 文檔非常大,不可能将xml所有資料加載到記憶體 

SAX 思想:一邊解析 ,一邊處理,一邊釋放記憶體資源 ---- 不允許在記憶體中保留大規模xml 資料

StAX The Stream API for XML ----- XML 流 API ---- JDK6.0新特性

STAX 是一種 拉模式 XML 解析方式,SAX 是一種 推模式 XML 解析方式(SAX性能不如STAX,STAX技術較新)

注解:

推push模式:由伺服器為主導,向用戶端主動發送資料( 推送 )

拉pull模式: 由用戶端為主導,主動向伺服器申請資料( 輪詢 )

三種解析開發包

掌握了三種思想後,程式員在實際開發中,使用已經開發好工具包 ----- JAXP 、DOM4j 、XML PULL

注解:

解析方式 與 解析開發包 關系?

解析方式是解析xml 思想,沒有具體代碼

解析開發包是解析xml思想具體代碼實作。

JAXP 是sun官方推出實作技術 同時支援 DOM SAX STAX

DOM4j 是開源社群開源架構  支援 DOM 解析方式

XML PULL Android 移動裝置内置xml 解析技術 支援 STAX 解析方式

DOM和SAX/STAX差別

|--DOM 支援回寫

      |--會将整個XML載入記憶體,以樹形結構方式存儲

      |--XML比較複雜的時候,或者當你需要随機處理文檔中資料的時候不建議使用

|--SAX/STAX

      |--相比DOM是一種更為輕量級的方案

      |--采用穿行方法讀取 -- 檔案輸入流(位元組、字元)讀取

      |--程式設計較為複雜

      |--無法再讀取過程中修改XML資料

注意:

當SAX和STAX 讀取xml資料時,如果讀取到記憶體資料不釋放 ----- 記憶體中将存在整個xml文檔資料(這種情況下就類似DOM 可以支援修改和回寫)

DOM、SAX、STAX 在實際開發中選擇?

在javaee日常開發中 ---- 優先使用DOM (程式設計簡單)

當xml 文檔資料非常多,不可能使用DOM ---造成記憶體溢出  ------ 優先使用STAX 

移動開發 使用 STAX ---- Android XML PULL 

JAXP DOM 解析

JAXP開發包簡介

JAXP(Java API for XML Processing):

    DOM、SAX、STAX 隻是XML解析方式,沒有API

    JAXP是 Sun 提供的一套XML解析API,它很好的支援DOM和SAX解析方式,JDK6.0開始支援STAX解析方式,JAXP 開發包是JavaSE的一部分,它由javax.xml、org.w3c.dom 、org.xml.sax 包及其子包組成,在 javax.xml.parsers 包中,定義了幾個工廠類,程式員調用這些工廠類,可以得到對xml文檔進行解析的 DOM 或 SAX 的解析器對象。

JAXP 開發 進行 xml解析 軟體包: 

javax.xml.parsers  存放 DOM 和 SAX 解析器 

javax.xml.stream  存放 STAX 解析相關類 

org.w3c.dom 存放DOM解析時 資料節點類

org.xml.sax 存放SAX解析相關工具類

DOM解析模型

DOM 是以層次結構組織的節點或資訊片斷的集合,是 XML 資料的一種樹型表示

XML文檔中所有的元素、屬性、文本都會被解析成node節點 ---- 進而在記憶體中形成XML文檔樹型模型 ---- 所有的解析操作都圍繞着這個模型進行

(屬性節點不屬于任何節點的父節點或者子節點!)

節點之間關系:parent、children、sibling(兄弟)

JavaEE實戰——XML文檔DOM、SAX、STAX解析方式詳解前言簡介JAXP DOM 解析JAXP SAX 解析XML PULL 解析綜合案例

DOM 解析快速入門

1、建立 xml 文檔 books.xml 

在企業實際開發中,為了簡化xml 生成和解析 ---- xml 資料檔案通常不使用限制的

2、使用DOM解析xml 

将整個xml文檔加載到記憶體中 : 工廠 --- 解析器 ---解析加載 

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();//工廠
DocumentBuilder builder = builderFactory.newDocumentBuilder();//解析器
Document document = builder.parse("books.xml");//裝載進記憶體           

3、Document通過 getElementsByTagName("")  獲得 指定标簽的節點集合 NodeList 

通過 NodeList 提供 getLength 和 item周遊 節點集合

周遊ArrayList
for (int i=0;i<arraylist.size();i++){
   arraylist.get(i);
}

周遊NodeList
for (int i=0;i<nodelist.getLength();i++){
  nodelist.item(i);  ----- 将周遊的每個節點轉換子接口類型
}           

什麼是 Node? 

對于xml 來說,xml所有資料都是node節點 (元素節點、屬性節點、文本節點、注釋節點、CDATA節點、文檔節點)

Element Attr Text Comment CDATASection Document  ----- 都是 Node 子接口

JavaEE實戰——XML文檔DOM、SAX、STAX解析方式詳解前言簡介JAXP DOM 解析JAXP SAX 解析XML PULL 解析綜合案例

、Node對象提供了一系列常量來代表節點的類型(檢視org.w3c.dom.Node接口源碼):

JavaEE實戰——XML文檔DOM、SAX、STAX解析方式詳解前言簡介JAXP DOM 解析JAXP SAX 解析XML PULL 解析綜合案例

當開發人員獲得某個Node類型後,就可以把Node節點轉換成相應的節點對象(Element、Attr、Text)。

注解:

node有三個通用API : 

      |--getNodeName():傳回節點的名稱

      |--getNodeType():傳回節點的類型 ---- ELEMENT_NODE=1、ATTRIBUTE_NODE=2、TEXT_NODE=3

      |--getNodeValue():傳回節點的值  ---- 所有元素節點value都是 null

另外,對于元素節點ELEMENT來說:

|--獲得元素節點中的屬性值

      |--element.getAttribute(屬性名稱)

|--獲得元素節點内部文本内容

      |--element.getTextContent()

      |--element.getFirstChild().getNodeValue()

代碼示例:

books.xml:

<?xml version="1.0" encoding="UTF-8"?>
<books>
	<book>
		<name>java程式設計基礎</name>
		<price>80</price>
	</book>
	<book>
		<name>java進階應用</name>
		<price>100</price>
	</book>
</books>           

測試程式:

package cn.itcast.dom.jaxp;

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class DOMTest {
	@Test
	// 查詢 java程式設計基礎 書 售價
	public void demo3() throws Exception {
		// 裝載xml 加載記憶體 --- Document對象
		DocumentBuilderFactory builderFactory = DocumentBuilderFactory
				.newInstance();
		DocumentBuilder builder = builderFactory.newDocumentBuilder();
		Document document = builder.parse("books.xml");

		// 利用全局查詢 鎖定 每個name節點
		NodeList nodelist = document.getElementsByTagName("name");
		for (int i = 0; i < nodelist.getLength(); i++) {
			Element name = (Element) nodelist.item(i);
			if (name.getTextContent().equals("java程式設計基礎")) {
				// 圖書 找到了
				// price 是 name 節點 兄弟的兄弟,三個換行符也是子節點
				Element price = (Element) name.getNextSibling()
						.getNextSibling();
				System.out.println(price.getTextContent());
			}
		}
	}

	@Test
	// 查詢 java程式設計基礎 書 售價
	public void demo2() throws Exception {
		// 裝載xml 加載記憶體 --- Document對象
		DocumentBuilderFactory builderFactory = DocumentBuilderFactory
				.newInstance();
		DocumentBuilder builder = builderFactory.newDocumentBuilder();
		Document document = builder.parse("books.xml");

		// 全局查詢 作為程式 切入
		NodeList nodelist = document.getElementsByTagName("book");
		// 周遊 強制轉換 Element
		for (int i = 0; i < nodelist.getLength(); i++) {
			Element book = (Element) nodelist.item(i);
			// 找 哪個 book 節點 當中 name 節點值 java程式設計基礎 ---- 查找book的name 子節點
			NodeList chidren = book.getChildNodes();
			//System.out.println(chidren.getLength());//注意:回車、空格也是子元素
			Element name = (Element) chidren.item(1); // book的第二個子節點就是name
			if (name.getTextContent().equals("java程式設計基礎")) {
				// 目前for循環 這本書 是目标圖書
				// 列印圖書價格 price 是 book 第四個子節點
				Element price = (Element) chidren.item(3);
				System.out.println(price.getTextContent());
			}
		}
	}

	@Test
	public void demo1() throws Exception {
		// 通過DOM 解析 XML --- 載入整個xml 工廠 -- 解析器 --- 加載

		// 構造工廠
		DocumentBuilderFactory builderFactory = DocumentBuilderFactory
				.newInstance();
		// 通過工廠 獲得解析器
		DocumentBuilder builder = builderFactory.newDocumentBuilder();

		// 使用解析器 加載 xml文檔
		Document document = builder.parse("books.xml");

		// Document代表整個xml 文檔,通過操作Document,操作xml資料

		// 将所有圖書名稱列印出來
		// 這裡 nodelist 代表節點的集合
		// 查詢所有 name标簽
		NodeList nodelist = document.getElementsByTagName("name");
		// 周遊集合中 所有 node
		System.out.println("圖書name節點數量:" + nodelist.getLength());
		for (int i = 0; i < nodelist.getLength(); i++) {
			// 獲得每個 node 節點
			Node node = nodelist.item(i); // 這裡每個node 都是 <name></name> ---- 元素
			Element e = (Element) node; // 将 節點轉換為 子類型 節點
			System.out.println(e.getNodeName()); // 節點元素名稱
			System.out.println(e.getNodeType()); // 節點元素 類型
			System.out.println(e.getNodeValue()); // 節點元素 值
			// 輸出 name 元素 子節點文本節點值
			System.out.println(e.getFirstChild().getNodeValue());
			System.out.println(e.getTextContent());
			System.out.println("------------------------------------");
		}
	}
}           

DOM 程式設計思路小結

1、裝載XML文檔 ---- Document (工廠--解析器--解析加載)

2、Document 獲得指定元素 ----- getElementsByTagName (傳回 NodeList)

3、周遊NodeList 獲得 每個 Node

4、将每個Node 強制轉換 Element 

5、通過元素節點API 操作屬性和文本内容 

      |--getAttribute  獲得屬性值

      |--getTextContent 獲得元素内部文本内容

這其中,第一步是固定套路:

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();//工廠
DocumentBuilder builder = builderFactory.newDocumentBuilder();//解析器
Document document = builder.parse("books.xml");//裝載進記憶體           

DOM的增删改查 ---- CURD

XML元素查詢

節點對象的查詢總結:

    先用全局查找鎖定範圍,再用相對關系查找 得到需要資料。

|--全局查找元素節點

      |--document.getElementByTagName

      |--document.getElementById( 需要帶限制的XML)

|--相對節點位置查找節點

      |--getChildNodes():傳回這個節點的所有子節點清單

      |--getFirstChild():傳回這個節點的第一個子節點

      |--getParentNode():傳回這個節點的父節點對象

      |--getNextSibling():傳回這個節點的下一個兄弟節點(注意空白也是節點)

      |--getPreviousSibling():傳回這個節點的前一個兄弟節點

注意:getElementById 方法 必須用于帶有限制 xml文檔中 !!!!!!!

例如:

books.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE books [
	<!ELEMENT books (book+) >
	<!ELEMENT book (name,price) >
	<!ELEMENT name (#PCDATA) >
	<!ELEMENT price (#PCDATA) >
	<!ATTLIST book
	   id ID #REQUIRED <!--就是這個東西-->
	>
]>
<books>
	<book id="b001">
		<name>java程式設計基礎</name>
		<price>80</price>
	</book>
	<book id="b002">
		<name>java進階應用</name>
		<price>100</price>
	</book>
</books>           

getElementById 代碼示例:

package cn.itcast.dom.jaxp;

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class DOMTest {
	@Test
	// getElementById 用法 --- 查找 id b002 圖書 名稱
	public void demo4() throws Exception {
		// 裝載xml 加載記憶體 --- Document對象
		DocumentBuilderFactory builderFactory = DocumentBuilderFactory
				.newInstance();
		DocumentBuilder builder = builderFactory.newDocumentBuilder();
		Document document = builder.parse("books.xml");

		// 直接通過id 查找 ----- 文檔必須使用 限制 --- 不用限制xml文檔 不能 使用getElementById
		Element book = document.getElementById("b002");
		System.out.println(book);
		System.out.println(book.getChildNodes().item(1).getTextContent());
	}
           

所有開發語言預設支援DTD,當使用Schema時,單獨程式設計導入schema !

如何對xml檔案進行schema限制?

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
StreamSource ss = new StreamSource("books.xsd");
Schema schema = factory.newSchema(ss);
builderFactory.setSchema(schema);
builderFactory.setNamespaceAware(true);           

XML回寫

XML DOM 增加 、修改 和 删除操作 ------ 操作 記憶體中文檔對象 ---- 操作記憶體結束後要回寫進某一檔案中

更新XML文檔步驟:

|--javax.xml.transform包中的Transformer類用于把代表XML檔案的Document對象轉換成XML格式進行輸出

|--Transformer對象通過TransformerFactory獲得

|--Transformer類通過transform方法完成轉換操作,該方法接收一個源和一個目的地。我們可以通過:

      |--javax.xml.transform.dom.DOMSource類來關聯要轉換的document對象, 

      |--javax.xml.transform.stream.StreamResult 對象來表示資料的目的地。

代碼示例:

package cn.itcast.dom.jaxp;

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class DOMTest {
	@Test
	// 将 books.xml 加載記憶體中,将文檔内容寫入另一個xml books_bak.xml(回寫)
	public void demo5() throws Exception, IOException {
		// 将 文檔 載入記憶體
		DocumentBuilderFactory builderFactory = DocumentBuilderFactory
				.newInstance();
		DocumentBuilder builder = builderFactory.newDocumentBuilder();
		Document document = builder.parse("books.xml");

		// 回寫xml 用到 Transformer
		TransformerFactory transformerFactory = TransformerFactory
				.newInstance();
		Transformer transformer = transformerFactory.newTransformer();

		DOMSource domSource = new DOMSource(document);// 用document構造資料源
		StreamResult result = new StreamResult(new File("books_bak.xml"));

		transformer.transform(domSource, result);
	}
}           

其實你可以發現回寫也是固定套路:

TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource domSource = new DOMSource(document);// 用document構造資料源
StreamResult result = new StreamResult(new File("books_bak.xml"));
transformer.transform(domSource, result);           

XML元素添加

|--建立節點元素

      |--document.createXXX()建立節點

|--将節點元素加入指定位置

      |--element.getDocumentElement()獲得根節點

      |--element.appendChild(org.w3c.dom.Node)添加節點

|--回寫XML

XML元素修改

|--加載xml到記憶體

|--查詢到指定元素 

|--修改元素的屬性值

      |--element.setAttribute(name,value);

|--修改元素内文本内容

      |--element.setTextContent(value);

|--回寫XML

XML元素删除

|--删除節點.getParentNode().removeChild(删除節點)

      (删除必須通過父節點、注意每次删完之後修複nodelist長度!)

代碼示例:

package cn.itcast.dom.jaxp;

import java.io.File;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 * CURD create update read delete
 * 
 * @author seawind
 * 
 */
public class DOMCURDTest {
	@Test
	// 删除所有 java書名 ----- 圖書
	public void testDelete() throws Exception {
		// 加載xml 到記憶體
		DocumentBuilderFactory builderFactory = DocumentBuilderFactory
				.newInstance();
		DocumentBuilder builder = builderFactory.newDocumentBuilder();
		Document document = builder.parse("books.xml");

		NodeList nodelist = document.getElementsByTagName("name");
		for (int i = 0; i < nodelist.getLength(); i++) {
			Element name = (Element) nodelist.item(i);
			if (name.getTextContent().contains("java")) {
				// 這本書删除 --- 通過name 獲得圖書
				Element book = (Element) name.getParentNode();
				// 删除 必須 通過父節點
				book.getParentNode().removeChild(book);
				i--; // 修複list長度
			}
		}

		// 回寫
		TransformerFactory transformerFactory = TransformerFactory
				.newInstance();
		Transformer transformer = transformerFactory.newTransformer();
		DOMSource domSource = new DOMSource(document);// 用document構造資料源
		StreamResult result = new StreamResult(new File("books_bak.xml"));
		transformer.transform(domSource, result);
	}

	@Test
	// 将 java進階應用 價格上調 20%
	public void testUpdate() throws Exception {
		// 加載xml 到記憶體
		DocumentBuilderFactory builderFactory = DocumentBuilderFactory
				.newInstance();
		DocumentBuilder builder = builderFactory.newDocumentBuilder();
		Document document = builder.parse("books.xml");

		// 查找 java進階應用書
		NodeList nodelist = document.getElementsByTagName("name");
		for (int i = 0; i < nodelist.getLength(); i++) {
			Element name = (Element) nodelist.item(i);
			if (name.getTextContent().equals("java進階應用")) {
				// 找到了 --- 獲得價格節點
				Element price = (Element) name.getNextSibling()
						.getNextSibling();
				double money = Double.parseDouble(price.getTextContent());
				money = money * 1.2;

				price.setTextContent(money + "");
			}
		}

		// 回寫
		TransformerFactory transformerFactory = TransformerFactory
				.newInstance();
		Transformer transformer = transformerFactory.newTransformer();
		DOMSource domSource = new DOMSource(document);// 用document構造資料源
		StreamResult result = new StreamResult(new File("books_bak.xml"));
		transformer.transform(domSource, result);
	}

	@Test
	// 向xml 添加一個 book元素
	public void testAdd() throws Exception {
		// 1 将原來 books.xml 加載到内容
		DocumentBuilderFactory builderFactory = DocumentBuilderFactory
				.newInstance();
		DocumentBuilder builder = builderFactory.newDocumentBuilder();
		Document document = builder.parse("books.xml");

		// 2、添加節點 建立節點 books
		Element newBook = document.createElement("book"); // <book></book>
		newBook.setAttribute("id", "b003");

		// 建立name節點
		Element newName = document.createElement("name"); // <name></name>
		newName.setTextContent("程式設計高手秘笈");

		// 将 新 name 放入 新 book
		newBook.appendChild(newName);

		// 3、添加節點到指定位置 ---- 獲得books根節點
		Element root = document.getDocumentElement();
		root.appendChild(newBook);

		// 4、回寫xml
		TransformerFactory transformerFactory = TransformerFactory
				.newInstance();
		Transformer transformer = transformerFactory.newTransformer();
		DOMSource domSource = new DOMSource(document);// 用document構造資料源
		StreamResult result = new StreamResult(new File("books_bak.xml"));
		transformer.transform(domSource, result);
	}
}           

DOM總結:

JavaEE實戰——XML文檔DOM、SAX、STAX解析方式詳解前言簡介JAXP DOM 解析JAXP SAX 解析XML PULL 解析綜合案例

JAXP SAX 解析

SAX 和 STAX 都是 基于事件驅動 ----- SAX推模式 STAX拉模式

SAX解析處理器的常用事件

|--DefaultHandler類(在 org.xml.sax.helpers 軟體包中)來實作所有這些回調,并提供所有回調方法預設的空實作 

      |--startDocument()  ---- 文檔開始事件

      |--startElement() ---- 元素開始事件

      |--characters() ---- 文本元素事件

      |--endElement() ---- 元素結束事件

      |--endDocument()  ----- 文檔結束事件

SAX解析原理

SAX和DOM不同:

DOM解析器 ---- 将整個XML文檔全部加載到記憶體,傳回文檔對象Document

      解析器DocumentBuilder ---- Document document = builder.parse(file)

SAX解析器 ---- 一邊讀取XML一邊解析一邊處理,并沒有傳回值

      解析器SAXParser ---- 将XML文檔和文檔解析處理器(DefaultHandler及其子類)同時傳遞給SAX解析器 ---- 解析器調用處理器相應的事件處理方法來處理文檔

JavaEE實戰——XML文檔DOM、SAX、STAX解析方式詳解前言簡介JAXP DOM 解析JAXP SAX 解析XML PULL 解析綜合案例

為什麼說SAX是推模式解析? 解析器控制xml檔案解析,由解析器調用相應事件方法

由位于伺服器端的解析器内部主導的事件方法調用 ---- 推模式

SAX解析器采用了基于事件的模型,它在解析XML文檔的時候可以觸發一系列的事件,發生相應事件時,将調用一個回調方法,例如:

<?xml version=“1.0” encoding=“utf-8”?>
<config>
	<server>UNIX</server>
</config>           

依次觸發的事件:

Start document

Start element (config)

Characters (whitespace)

Start element (server)

Characters (UNIX)

End element (server)

Characters (whitespace)

End element (config)

End document

代碼示例:

server.xml:

<?xml version="1.0" encoding="UTF-8"?>
<config>
	<server id="s100">UNIX</server>
</config>           

SAX解析:

package cn.itcast.sax.jaxp;

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 執行個體
 * 
 * @author seawind
 * 
 */
public class SAXTest {
	public static void main(String[] args) throws Exception {
		// 1、工廠
		SAXParserFactory factory = SAXParserFactory.newInstance();
		// 2、通過工廠獲得解析器
		SAXParser parser = factory.newSAXParser();

		// 3 、建立 Handler
		MyHandler handler = new MyHandler();

		// 4、将xml 文檔 和 handler 同時傳遞給 解析器
		parser.parse("server.xml", handler);
	}
}

/**
 * 繼承 DefaultHandler 重寫 5 個事件方法
 * 
 * @author seawind
 * 
 */
class MyHandler extends DefaultHandler {
	@Override
	public void startDocument() throws SAXException {
		System.out.println("start document...");
	}

	@Override
	public void startElement(String uri, String localName, String qName,
			Attributes attributes) throws SAXException {
		System.out.println("start element(" + qName + ")...");
		// 列印server元素 id 屬性 --- 判斷目前開始元素是 server
		if (qName.equals("server")) {
			System.out.println("id屬性的值:" + attributes.getValue("id"));
		}
	}

	@Override
	public void characters(char[] ch, int start, int length)
			throws SAXException {
		String content = new String(ch, start, length);
		System.out.println("characters: " + content);
	}

	@Override
	public void endElement(String uri, String localName, String qName)
			throws SAXException {
		System.out.println("end element(" + qName + ")...");
	}

	@Override
	public void endDocument() throws SAXException {
		System.out.println("end document...");
	}

}           

使用SAX方式解析XML步驟

|--使用SAXParserFactory建立SAX解析工廠

      |--SAXParserFactory spf = SAXParserFactory.newInstance();

|--通過SAX解析工廠得到解析器對象

      |--SAXParser sp = spf.newSAXParser();

|--通過解析器對象解析xml檔案

      |--sp.parse("book.xml“,new XMLContentHandler());

      |--這裡的XMLContentHandler 繼承 DefaultHandler

            |--在startElement() endElement() 獲得 開始和結束元素名稱

            |--在characters() 獲得讀取到文本内容

            |--在startElement() 讀取屬性值 

XML PULL 解析

STAX 拉模式xml 解析方式 ----- 用戶端程式,自己控制xml事件,主動調用相應事件方法

XML PULL 解析器開發包簡介

當使用XML PULL,如果使用Android系統,系統内置無需下載下傳任何開發包;如果想JavaSE、JavaEE使用pull解析技術下載下傳單獨pull 開發工具包。、

xpp3 ----- XML Pull Parser 3 是pull API 代碼實作

使用pull 解析器

1、去網站上 下載下傳 pull 解析器的實作 xpp3 (Android 内置)

2、将 xpp3-1.1.3.4.C.jar 導入 java工程 

良好習慣:要導入jar包應當位于目前工程内部。

方法:在工程内建立lib檔案夾,将jar複制過來,然後将pull解析器xpp3.jar包添加至Java Build Path (Libraries--Add JARs 或右鍵jar包 Add to Build Path),這樣pull解析器才能使用。

注解:jar 包就是.class檔案 集合壓縮包 (采用zip格式壓縮)

3、建立pull 解析器 ---- XmlPullParser

4、将xml 文檔内容傳遞 pull 解析器

5、需要用戶端程式手動完成解析,XmlPullParser存放解析方法next(),用于解析器解析下一事件

STAX解析原理

Pull解析器 使用 stax 解析方式 ---- 拉模式解析

JavaEE實戰——XML文檔DOM、SAX、STAX解析方式詳解前言簡介JAXP DOM 解析JAXP SAX 解析XML PULL 解析綜合案例

SAX解析器當接收到XML檔案内容,伺服器端解析器SAXParser自動開始解析,自動解析過程中調用處理器相應方法 ---- 推模式

Pull采用将xml文檔傳遞解析器,解析器XmlPullParser不會自動解析,需要手動通過next觸發文檔解析事件,在用戶端代碼中擷取目前事件 ,進而調用相應事件處理方法。

為什麼 STAX 解析方式 效率 好于 SAX ?

1、SAX 無選擇性的,所有事件都會處理的解析方式,解析器控制事件的調用;StAX由使用者自主要制需要處理事件類型以及事件的調用。

2、在使用Stax進行資料解析時,随時終止解析。

使用XML Pull解析 XML

|--參考官方文檔

      |--http://www.xmlpull.org/v1/download/unpacked/doc/quick_intro.html

      |--Xpp3 XmlPullParser javadoc

|--關鍵代碼

      |--建立解析器工廠

            |--XmlPullParserFactory factory =  XmlPullParserFactory.newInstance();

            |--factory.setNamespaceAware(true); 

      |--根據工廠建立解析器

            |--XmlPullParser xpp = factory.newPullParser(); 

      |--讀取xml檔案

            |--xpp.setInput(inStream, "UTF-8");

      |--目前節點事件類型

            |--int eventType = xpp.getEventType();

      |--下一個節點事件

            |--eventType = xpp.next();

      |--獲得元素名稱

            |--xpp.getName();

      |--獲得标簽屬性值

            |--xpp.getAttributeValue

      |--獲得标簽後面文本内容

            |--xpp.nextText();

代碼示例:

books.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE books [
	<!ELEMENT books (book+) >
	<!ELEMENT book (name,price) >
	<!ELEMENT name (#PCDATA) >
	<!ELEMENT price (#PCDATA) >
	<!ATTLIST book
	   id ID #REQUIRED <!--就是這個東西-->
	>
]>
<books>
	<book id="b001">
		<name>java程式設計基礎</name>
		<price>80</price>
	</book>
	<book id="b002">
		<name>java進階應用</name>
		<price>100</price>
	</book>
	<book id="boo3">
		<name>程式設計高手秘笈</name>
		<price>200</price>
	</book>	
</books>           

周遊代碼執行個體、查詢某本書的價格:

package cn.itcast.stax.pull;

import java.io.FileInputStream;

import org.junit.Test;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

/**
 * 通過 pull 解析器 解析 xml
 * 
 * @author seawind
 * 
 */
public class PullTest {
	@Test
	// 通過 pull 解析技術 檢視 "程式設計高手秘笈" 價格
	public void demo2() throws Exception {
		// 1. 建立 pull 解析器
		XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
		XmlPullParser parser = factory.newPullParser();
		// 2. 将 xml文檔傳遞 解析器
		parser.setInput(new FileInputStream("books.xml"), "utf-8");

		// 通過循環 驅動事件解析
		int event;

		// 查找name 辨別位
		boolean isFound = false;
		while ((event = parser.getEventType()) != XmlPullParser.END_DOCUMENT) {
			// 獲得 開始元素 name
			if (event == XmlPullParser.START_TAG
					&& parser.getName().equals("name")) {
				// 獲得元素後面文本
				String bookname = parser.nextText();
				if (bookname.equals("程式設計高手秘笈")) {
					isFound = true;
					// 這本書就是我要找到
					// parser.next();
					// System.out.println(parser.getEventType());
					// parser.next(); // price 開始
					// System.out.println(parser.getEventType());
					// String money = parser.nextText();//太麻煩,用辨別位簡單
					// System.out.println(money);
				}
			}

			if (event == XmlPullParser.START_TAG
					&& parser.getName().equals("price") && isFound) {
				System.out.println(parser.nextText());
				break;
			}

			parser.next();
		}
	}

	@Test
	public void demo1() throws Exception {
		// 1、建立 xml pull 解析器
		// 工廠
		XmlPullParserFactory xmlPullParserFactory = XmlPullParserFactory
				.newInstance();

		// 通過工廠 獲得解析器
		XmlPullParser parser = xmlPullParserFactory.newPullParser();

		// 2、将 xml 檔案 傳遞 解析器
		parser.setInput(new FileInputStream("books.xml"), "utf-8");

		// pull 解析器用得是 拉模式 資料 解析
		int event;

		while ((event = parser.getEventType()) != XmlPullParser.END_DOCUMENT) {
			//System.out.println(event);
			
			// 列印哪個元素開始了 ---- 判斷目前事件 是 元素開始事件
			if (event == XmlPullParser.START_TAG) {
				// 所有資料 從解析器 獲得
				System.out.println(parser.getName() + "元素開始了...");
			}

			// 列印 哪個 元素 結束了
			if (event == XmlPullParser.END_TAG) {
				System.out.println(parser.getName() + "元素 結束了...");
			}

			// 處理下一個事件
			parser.next();
		}

		// parser.getEventType()獲得目前事件類型
		// 可以通過檢視XmlPullParser源碼得到各常量代表意義
		// int event = parser.getEventType();
		//
		// System.out.println(event);//START_DOCUMENT = 0
		//
		// parser.next(); // 解析器解析下一個事件
		//
		// int event2 = parser.getEventType();
		//
		// System.out.println(event2);//START_TAG = 2
		//
		// parser.next();
		//
		// int event3 = parser.getEventType();
		//
		// System.out.println(event3);//TEXT = 4

	}
}           

XML PULL 生成XML文檔

Pull 解析器 生成 xml 文檔功能 ---- 通過 XmlSerializer 生成 xml 文檔

解析xml :文檔開始、元素開始、文本元素、元素結束、文檔結束

生成xml :生成文檔聲明(文檔開始),元素開始、文本内容、元素結束 、文檔結束

代碼示例:

1、生成簡單xml

2、通過對象資料生成xml

3、通過對象List資料生成xml

      ---- 序列化 XmlSerializer

package cn.itcast.stax.pull;

import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;

import org.junit.Test;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;

import cn.itcast.domain.Company;

/**
 * 生成 xml
 * 
 * @author seawind
 * 
 */
public class SerializerTest {
	@Test
	// 根據 List<Company> 生成xml
	public void demo3() throws Exception {
		List<Company> companies = new ArrayList<Company>();

		Company company = new Company();
		company.setName("傳智播客");
		company.setPnum(200);
		company.setAddress("西二旗軟體園!");

		Company company2 = new Company();
		company2.setName("CSDN");
		company2.setPnum(1000);
		company2.setAddress("西二旗 軟體園 ");

		companies.add(company);
		companies.add(company2);

		// 序列化對象
		XmlSerializer serializer = XmlPullParserFactory.newInstance()
				.newSerializer();

		// 設定輸出檔案
		serializer.setOutput(new FileOutputStream("company.xml"), "utf-8");

		serializer.startDocument("utf-8", true);

		serializer.startTag(null, "companies");

		// 周遊list集合
		for (Company c : companies) {
			serializer.startTag(null, "company");

			serializer.startTag(null, "name");
			serializer.text(c.getName());
			serializer.endTag(null, "name");

			serializer.startTag(null, "pnum");
			serializer.text(c.getPnum() + "");
			serializer.endTag(null, "pnum");

			serializer.startTag(null, "address");
			serializer.text(c.getAddress());
			serializer.endTag(null, "address");

			serializer.endTag(null, "company");
		}

		serializer.endTag(null, "companies");

		serializer.endDocument();

	}

	@Test
	// 根據company對象資料生成xml
	public void demo2() throws Exception {
		Company company = new Company();
		company.setName("傳智播客");
		company.setPnum(200);
		company.setAddress("西二旗軟體園!");

		/*
		 * <company>
		 * 
		 * <name>傳智播客</name>
		 * 
		 * <pnum>200</pnum>
		 * 
		 * <address>西二旗軟體園</address>
		 * 
		 * </company>
		 */

		// 獲得序列化對象
		XmlSerializer serializer = XmlPullParserFactory.newInstance()
				.newSerializer();

		// 傳遞 輸出目标檔案 給序列化對象
		serializer.setOutput(new FileOutputStream("company.xml"), "utf-8");

		serializer.startDocument("utf-8", true);

		serializer.startTag(null, "company");

		serializer.startTag(null, "name");
		serializer.text(company.getName());
		serializer.endTag(null, "name");

		serializer.startTag(null, "pnum");
		serializer.text(company.getPnum() + "");
		serializer.endTag(null, "pnum");

		serializer.startTag(null, "address");
		serializer.text(company.getAddress());
		serializer.endTag(null, "address");

		serializer.endTag(null, "company");

		serializer.endDocument();
	}

	@Test
	public void demo1() throws Exception {
		// 獲得XmlSerializer對象
		XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
		XmlSerializer serializer = factory.newSerializer();

		// 設定序列化輸出文檔
		serializer.setOutput(new FileOutputStream("company.xml"), "utf-8");

		// 文檔開始
		serializer.startDocument("utf-8", true);

		// 元素開始
		serializer.startTag(null, "company"); // 沒有命名空間 ,"" 或者 null

		// 文本元素
		serializer.text("傳智播客");

		// 元素結束
		serializer.endTag(null, "company");

		// 文檔結束
		serializer.endDocument();

		/*
		 * <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
		 * 
		 * <company>
		 * 
		 * 傳智播客
		 * 
		 * </company>
		 */
	}
}           

STAX的增删改查 ---- CURD

對xml檔案通過pull解析器進行CURD操作原理:

JavaEE實戰——XML文檔DOM、SAX、STAX解析方式詳解前言簡介JAXP DOM 解析JAXP SAX 解析XML PULL 解析綜合案例

當下問題:pull解析器封裝List對象過程 ---- 如何将XML資料 --> List<Object>

JavaEE實戰——XML文檔DOM、SAX、STAX解析方式詳解前言簡介JAXP DOM 解析JAXP SAX 解析XML PULL 解析綜合案例

将xml資料通過pull解析器生成List集合,代碼示例:

company源碼:

package cn.itcast.domain;

/**
 * 公司資料類
 * 
 * @author seawind
 * 
 */
public class Company {
	private String name;
	private int pnum;//人數
	private String address;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getPnum() {
		return pnum;
	}

	public void setPnum(int pnum) {
		this.pnum = pnum;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

}           

測試代碼:

package cn.itcast.stax.pull;

import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;

import org.junit.Test;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import cn.itcast.domain.Company;

public class PullCURD {
	@Test
	// 将xml中資料 ---- List集合對象
	public void demo1() throws Exception {
		List<Company> companies = new ArrayList<Company>();
		Company company = null;//定義一個臨時Company引用

		// 獲得解析器
		XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
		XmlPullParser parser = factory.newPullParser();

		// 向解析器傳入xml檔案
		parser.setInput(new FileInputStream("company.xml"), "utf-8");

		// 周遊解析
		int event;
		while ((event = parser.getEventType()) != XmlPullParser.END_DOCUMENT) {

			if (event == XmlPullParser.START_TAG
					&& parser.getName().equals("company")) {
				// company 開始 建立 company 對象
				company = new Company();
			}

			if (event == XmlPullParser.START_TAG
					&& parser.getName().equals("name")) {
				// name 元素開始 -- 封裝name屬性
				company.setName(parser.nextText());
			}

			if (event == XmlPullParser.START_TAG
					&& parser.getName().equals("pnum")) {
				company.setPnum(Integer.parseInt(parser.nextText()));
			}

			if (event == XmlPullParser.START_TAG
					&& parser.getName().equals("address")) {
				company.setAddress(parser.nextText());
			}

			if (event == XmlPullParser.END_TAG
					&& parser.getName().equals("company")) {
				// company 結束
				companies.add(company);
			}

			parser.next();
		}

		for (Company c : companies) {
			System.out.println(c.getName());
			System.out.println(c.getPnum());
			System.out.println(c.getAddress());
			System.out.println("----------------------");
		}

	}
}           

當記憶體中讀取到List<Object>後,就可以CURD,最後序列化到XML檔案中即可。

因為上述方法經常使用,是以建議在程式中抽取這兩個方法 ----- 1. xml ---> List對象  2. List對象生成xml 

* XmlPullUtils通用程式(反射+泛型) ------ 編寫一個工具類 以後pull解析 

package cn.itcast.stax.pull;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;

import cn.itcast.domain.Company;

/**
 * 工具類 抽取兩個方法 :1、xml --> List 2 List --> XML
 * 
 * @author seawind
 * 
 */
public class PullUtils {
	/**
	 * 接收xml檔案,傳回 List集合
	 * 
	 * @param fileName
	 * @return
	 * @throws Exception
	 */
	public static List<Company> parseXml2List(String fileName) throws Exception {
		List<Company> companies = new ArrayList<Company>();

		// 獲得解析器
		XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
		XmlPullParser parser = factory.newPullParser();

		// 設定 xml 輸入檔案
		parser.setInput(new FileInputStream(fileName), "utf-8");

		// 解析周遊
		int event;
		// 定義一個臨時Company 引用
		Company company = null;
		while ((event = parser.getEventType()) != XmlPullParser.END_DOCUMENT) {
			// 将每個<company> 元素封裝 Company對象
			// 1、在company開始時候,建立對象
			if (event == XmlPullParser.START_TAG
					&& parser.getName().equals("company")) {
				company = new Company();
			}

			// 2、讀取name元素時,向company對象中封裝 name屬性
			if (event == XmlPullParser.START_TAG
					&& parser.getName().equals("name")) {
				company.setName(parser.nextText());
			}

			// 3、讀取pnum元素時,向company對象儲存pnum屬性
			if (event == XmlPullParser.START_TAG
					&& parser.getName().equals("pnum")) {
				company.setPnum(Integer.parseInt(parser.nextText()));
			}

			// 4、讀取address元素,向company封裝 address屬性
			if (event == XmlPullParser.START_TAG
					&& parser.getName().equals("address")) {
				company.setAddress(parser.nextText());
			}

			// 5、讀取company元素結束時,将company對象加入集合
			if (event == XmlPullParser.END_TAG
					&& parser.getName().equals("company")) {
				companies.add(company);
			}

			// 解析 下一個 事件
			parser.next();
		}

		return companies;
	}

	/**
	 * 同時接收xml檔案和List集合,将集合中資料寫入xml檔案中
	 * 
	 * @param companies
	 * @param fileName
	 * @throws Exception
	 */
	public static void serializeList2Xml(List<Company> companies,
			String fileName) throws Exception {
		// 獲得序列化對象
		XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
		XmlSerializer serializer = factory.newSerializer();

		// 寫檔案之前,指定輸出檔案
		serializer.setOutput(new FileOutputStream(fileName), "utf-8");

		// 文檔開始
		serializer.startDocument("utf-8", true);

		// 根元素開始 companies
		serializer.startTag(null, "companies");

		// 周遊集合List ,每個List中Company對象 生成一個片段
		for (Company company : companies) {
			// company 開始
			serializer.startTag(null, "company");

			// name屬性開始
			serializer.startTag(null, "name");

			// 寫入name資料
			serializer.text(company.getName());

			// name屬性結束
			serializer.endTag(null, "name");

			// pnum 屬性開始
			serializer.startTag(null, "pnum");

			// 寫入pnum資料
			serializer.text(company.getPnum() + "");

			// pnum 屬性結束
			serializer.endTag(null, "pnum");

			// address屬性開始
			serializer.startTag(null, "address");

			// 寫入 address值
			serializer.text(company.getAddress());

			// address屬性結束
			serializer.endTag(null, "address");

			// company 結束
			serializer.endTag(null, "company");
		}

		// 根元素結束
		serializer.endTag(null, "companies");

		// 文檔結束
		serializer.endDocument();
	}
}           

對記憶體中List進行CURD操作 

package cn.itcast.stax.pull;

import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;

import org.junit.Test;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import cn.itcast.domain.Company;

/**
 * 完成pull解析器CURD操作
 * 
 * @author seawind
 * 
 */
public class PullCURD {
	@Test
	// CSDN 從清單删除
	public void testDelete() throws Exception {
		// 1、解析 xml 資料到記憶體 list
		List<Company> companies = PullUtils.parseXml2List("company.xml");

		// 2、從list集合中删除 csdn 的company對象
		for (Company company : companies) {
			if (company.getName().equals("CSDN")) {
				companies.remove(company);
				break;//不寫break,會報并發錯誤
			}
		}

		// 3、回寫xml
		PullUtils.serializeList2Xml(companies, "company_bak.xml");
	}

	@Test
	// 将傳智播客人數 200%
	public void testUpdate() throws Exception {
		// 1、解析 xml 資料到記憶體 list
		List<Company> companies = PullUtils.parseXml2List("company.xml");

		// 2、增長傳智播客人數 200%
		for (Company company : companies) {
			if (company.getName().equals("傳智播客")) {
				company.setPnum(company.getPnum() * 2);
			}
		}

		// 3、回寫xml
		PullUtils.serializeList2Xml(companies, "company_bak.xml");
	}

	@Test
	// 查詢 csdn人數
	public void testSelect() throws Exception {
		// 1、解析 xml 資料到記憶體 list
		List<Company> companies = PullUtils.parseXml2List("company.xml");
		// 2 周遊集合對象
		for (Company company : companies) {
			if (company.getName().equals("CSDN")) {
				System.out.println(company.getPnum());
			}
		}
	}

	@Test
	// 向 company插入一個公司資訊
	public void testAdd() throws Exception {
		// 1、解析 xml 資料到記憶體 list
		List<Company> companies = PullUtils.parseXml2List("company.xml");

		// 2、添加company對象
		Company company = new Company();
		company.setName("baidu");
		company.setPnum(5000);
		company.setAddress("百度大樓!");
		companies.add(company);

		// 3、将List對象回寫xml
		PullUtils.serializeList2Xml(companies, "company_bak.xml");
	}

	@Test
	// 測試工具類 PullUtils中方法
	public void demo2() throws Exception {
		// 将 company.xml 複制 company_bak.xml
		// 解析獲得集合
		List<Company> companies = PullUtils.parseXml2List("company.xml");
		// 将集合寫入 company_bak.xml
		PullUtils.serializeList2Xml(companies, "company_bak.xml");
	}
}           

綜合案例

    平時的開發過程中,經常涉及到xml檔案的解析,實作xml檔案到java bean的轉換。目前有個xml檔案,在不允許使用第三方jar的情況下解析xml檔案,并根據member節點建立member對象。檔案格式如下:

<?xml version="1.0" encoding="UTF-8"?>
<root>
	<member>
		<name>Jack1</name>
		<age>18</age>
		<grade>70</grade>
	</member>
	<member>
		<name>Appl2</name>
		<age>20</age>
		<grade>70</grade>
	</member>
	<member>
		<name>Appl2</name>
		<age>20</age>
		<grade>70</grade>
	</member>
	<member>
		<name>Appl2</name>
		<age>20</age>
		<grade>72</grade>
	</member>
	<member>
		<name>Adpl2</name>
		<age>20</age>
		<grade>73</grade>
	</member>
	<member>
		<name>ccpl2</name>
		<age>20</age>
		<grade>75</grade>
	</member>
	<member>
		<name>bppl2</name>
		<age>20</age>
		<grade>75</grade>
	</member>
</root>           

    在輸入檔案中(檔案類型為xml檔案),存放話務員的基本資訊。該檔案中的話務員資訊是亂序并且有可能重複的,現在需要輸出每位話務員的資訊,對于重複的資訊隻能輸出一次。要求如下:

    1、需要把話務員資訊使用集合類緩存起來,并且集合中的資訊必須唯一(姓名+年齡唯一)。

    2、輸出話務員資訊,輸出格式為:姓名(年齡):成績|姓名(年齡):成績,依次按照成績、姓名、年齡升序排列。

    3、啟動兩個線程分别做如下處理:

        線程一:對于話務員年齡小于(包含)18歲的,成績統一加10分。并把話務員資訊依次按照成績、姓名、年齡升序的方式輸出到一個隊列中。隊列的大小不能超過10個。

        線程二: 現有兩個分公司(A,B)依次選擇話務員,如:A選擇第一個話務員後,B再選擇一個,依次類推,直到話務員被選完。最後,分别輸出A,B兩個分公司所選擇的話務員資訊,輸出格式為:姓名(年齡):成績|姓名(年齡):成績,依次按照成績、姓名、年齡升序排列。

控制台輸出:

1、輸出話務員資訊,輸出格式為:姓名(年齡):成績|姓名(年齡):成績,依次按照成績、姓名、年齡升序排列。

2、輸出分公司A選擇的話務員資訊,輸出格式為:姓名(年齡):成績|姓名(年齡):成績,依次按照成績、姓名、年齡升序排列。

3、輸出分公司B選擇的話務員資訊,,輸出格式為:姓名(年齡):成績|姓名(年齡):成績,依次按照成績、姓名、年齡升序排列。

上面輸出的結果為:

Appl2(20):70|Jack1(18):70|Adpl2(20):73|bppl2(20):75|ccpl2(20):75

Appl2(20):70|bppl2(20):75|Jack1(18):80

Adpl2(20):73|ccpl2(20):75

分析:

題目要求 XML 文檔

1、将話務員資料從xml 檔案 讀取出來 ------ Java 對象 -----  很多 話務員 ----- Java對象 集合

* 集合中的資訊必須唯一(姓名+年齡唯一) ---- java 對象使用姓名+年齡 排重

2、輸出話務員資訊 ---  依次按照成績、姓名、年齡升序排列

Appl2(20):70|Jack1(18):70|Adpl2(20):73|bppl2(20):75|ccpl2(20):75

Appl2(20):70|Jack1(18):70|Adpl2(20):73|bppl2(20):75|ccpl2(20):75|

3、線程一:對于話務員年齡小于(包含)18歲的,成績統一加10分。并把話務員資訊依次按照成績、姓名、年齡升序的方式輸出到一個隊列中。隊列的大小不能超過10個。

4、線程二: 現有兩個分公司(A,B)依次選擇話務員 ,輸出格式為:姓名(年齡):成績|姓名(年齡):成績,依次按照成績、姓名、年齡升序排列。

注:

HashSet --- 排重集合 排重hashCode 和 equals

TreeSet ---- 即排重又排序集合 排重和排序 通過compareTo 

* 當排重和排序字段相同時 ---TreeSet ,否則不可以用TreeSet 

Comparable<E>接口中的排序方法 ---- public int compareTo(E o):

前-後 升序

後-前 降序

代碼示例:

Member.java:

package mytest;

public class Member implements Comparable<Member>, Cloneable {
	private String name;
	private int age;
	private int grade;

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Member other = (Member) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public int compareTo(Member o) {
		// 升序 目前元素 - 傳入元素 值
		// 成績升序
		int result1 = this.getGrade() - o.getGrade();
		if (result1 == 0) {
			// 姓名升序
			int result2 = this.name.compareTo(o.name);
			if (result2 == 0) {
				// 年齡升序
				int result3 = this.age - o.age;
				return result3;
			} else {
				return result2;
			}
		} else {
			return result1;
		}
	}

	public void setGrade(int grade) {
		this.grade = grade;
	}

	public int getGrade() {
		return grade;
	}

	@Override
	public Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}           

MemberTest.java

package mytest;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;

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

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class MemberTest {
	public static void main(String[] args) throws Exception {
		// 第一問 讀取xml資料進行排重
		// 将xml中資料 儲存 Member對象集合
		Set<Member> members = new HashSet<Member>();

		// 讀取xml --- JAXP DOM
		DocumentBuilderFactory builderFactory = DocumentBuilderFactory
				.newInstance();
		DocumentBuilder builder = builderFactory.newDocumentBuilder();
		Document document = builder.parse("member.xml");

		// 每一個<member>标簽 對應 member
		NodeList nodeList = document.getElementsByTagName("member");
		for (int i = 0; i < nodeList.getLength(); i++) {
			Element memberElement = (Element) nodeList.item(i);
			Member member = new Member();
			// 第二個元素 name
			member.setName(memberElement.getChildNodes().item(1)
					.getTextContent());
			// 第四個元素 age
			member.setAge(Integer.parseInt(memberElement.getChildNodes()
					.item(3).getTextContent()));
			// 第六個元素 grade
			member.setGrade(Integer.parseInt(memberElement.getChildNodes()
					.item(5).getTextContent()));

			// 将 member存入集合
			members.add(member);
		}
		// 檢查第一問結果:輸出應該是5
		//System.out.println(members.size());

		// 第二問 ,對 Set中member資料進行排序
		Set<Member> sortMembers = new TreeSet<Member>();
		sortMembers.addAll(members);
		for (Member member : sortMembers) {
			System.out.print(member.getName() + "(" + member.getAge() + "):"
					+ member.getGrade() + "|");
		}
		System.out.println();

		// 第三問
		// ,對于話務員年齡小于(包含)18歲的,成績統一加10分。
		//并把話務員資訊依次按照成績、姓名、年齡升序的方式輸出到一個隊列中。
		//隊列的大小不能超過10個
		new Thread(new MyThread1(sortMembers)).start();

		// 第四問 模拟選人
		Runnable a = new MyThread2(sortMembers, "A");
		Runnable b = new MyThread2(sortMembers, "B");

		new Thread(a).start();
		new Thread(b).start();
	}
}

class MyThread2 implements Runnable {

	private static List<Member> members;//static保證唯一
	private String companyName;

	private static boolean aturn = true;//static保證唯一

	public MyThread2(Set<Member> members, String companyName) {
		// 将set中元素轉存List中,實作有序取出元素remove(index)
		this.members = new ArrayList<Member>();
		this.members.addAll(members);

		this.companyName = companyName;
	}

	@Override
	public void run() {
		synchronized (members) {
			while (members.size() != 0) {
				// System.out.println(companyName + "執行");
				if (aturn && companyName.equals("A")) {
					Member m = members.remove(0);
					System.out.println("A公司選擇:" + m.getName());

					aturn = false;
					members.notify();
					try {
						System.out.println("A 等待");
						members.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				if (!aturn && companyName.equals("B")) {
					Member m = members.remove(0);
					System.out.println("B公司選擇:" + m.getName());

					aturn = true;
					members.notify();
					try {
						System.out.println("B 等待");
						members.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
		System.exit(0);
	}

}

class MyThread1 implements Runnable {

	private Set<Member> members;
	private Queue<Member> queue = new LinkedList<Member>();

	public MyThread1(Set<Member> members) {
		this.members = members;
	}

	@Override
	public void run() {
		// 加分排序
		Set<Member> sortMembers = new TreeSet<Member>();

		// 周遊原來的集合
		for (Member member : members) {
			if (member.getAge() <= 18) {
				// 加分 ---不能在原來對象裡加分
				Member m = null;
				try {
					m = (Member) member.clone();
					m.setGrade(m.getGrade() + 10);
					sortMembers.add(m);
				} catch (CloneNotSupportedException e) {
					e.printStackTrace();
				}
			} else {
				// 不需要克隆
				sortMembers.add(member);
			}
		}

		for (Member member : sortMembers) {
			System.out.print(member.getName() + "(" + member.getAge() + "):"
					+ member.getGrade() + "|");
			// 加入隊列
			queue.add(member);
		}
		System.out.println();
	}
}