前言
本文接着上一篇部落格進行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(兄弟)
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 子接口
、Node對象提供了一系列常量來代表節點的類型(檢視org.w3c.dom.Node接口源碼):
當開發人員獲得某個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總結:
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解析器 ---- 解析器調用處理器相應的事件處理方法來處理文檔
為什麼說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 解析方式 ---- 拉模式解析
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操作原理:
當下問題:pull解析器封裝List對象過程 ---- 如何将XML資料 --> List<Object>
将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();
}
}