使用JAXP處理XML文件 作者:賈波 作者簡(jiǎn)介 賈波,程序員,您可以通過(guò)mosaic@hotmail.com與他聯(lián)系。 簡(jiǎn)介 JAXP是Java API for XML Processing的縮寫(xiě)。JAXP API主要的部分在javax.xml.parsers 這個(gè)包中。在這個(gè)包中,向用戶(hù)提供了兩個(gè)最重要的工廠類(lèi),SAXParserFactory 和DocumentBuilderFactory,相應(yīng)地,提供了SAXParser 和DocumentBuilder兩個(gè)類(lèi)。 SAX是由XML-DEV定義的;DOM是由W3C定義的。讓我們來(lái)看看這些API庫(kù)。 - javax.xml.parsers
JAXP API, 定義個(gè)SAX和DOM的一個(gè)通用接口
- org.w3c.dom
定義了DOM中的所有組件
- org.xml.sax
定義了SAX的所有API
- javax.xml.transform
定義了XSLT API,使用它,你可以將XML轉(zhuǎn)化為一般的可視的頁(yè)面。 SAX指一種"事件驅(qū)動(dòng)"的處理方式,他對(duì)XML文件連續(xù)地一個(gè)對(duì)象一個(gè)對(duì)象地操作,由于它的這個(gè)特點(diǎn),所以它可以用于服務(wù)器端或者對(duì)速度有特殊要求的地方。 相比較而言DOM是個(gè)使用起來(lái)更簡(jiǎn)單些。他是將所有個(gè)XML數(shù)據(jù)全部讀到內(nèi)存里面,然后使用"樹(shù)"結(jié)構(gòu)將這些數(shù)據(jù)組織起來(lái),用戶(hù)可以對(duì)XML的數(shù)據(jù)進(jìn)行任意的操作。 至于XSLT,我們?cè)谶@里就不介紹太多,如果感興趣請(qǐng)參考相應(yīng)的資料。我們還是先看看SAX。 SAX SAX的框架輪廓 系統(tǒng)是從SAXParserFactory產(chǎn)生parser的實(shí)例開(kāi)始的。一個(gè)parser中包含了一個(gè)SAXReader對(duì)象,當(dāng)這個(gè)parser調(diào)用parse方法的時(shí)候,這個(gè)reader就調(diào)用回調(diào)方法已實(shí)現(xiàn)這個(gè)應(yīng)用;而這些方法呢?是定義在ContentHandler,ErrorHandler,DTDHandler and EntityResolver接口中的。 以下是對(duì)SAX API庫(kù)的概述: - SAXParserFactory
SAXParserFactory是一個(gè)根據(jù)系統(tǒng)屬性生成parser實(shí)例的一個(gè)對(duì)象。
- SAXParser
SAXParser是一個(gè)定義了不同種類(lèi)的parser()方法的接口。一般而言,你向parser傳XML數(shù)據(jù)后,使用DefaultHandler再來(lái)處理,系統(tǒng)就會(huì)調(diào)用一些合適的方法來(lái)處理XML文件,這樣的一種處理方法是最為簡(jiǎn)單的。
- SAXReader
SAXParser包含了一個(gè)SAXReader,通常你是不需要關(guān)心它的,但是當(dāng)你要使用SAXReader的getXMLReader()方法的時(shí)候,你就需要配置他。簡(jiǎn)言之,SAXParser就是一個(gè)與SAX事件通訊的處理器,這樣,你就可以使用自定義的handler。
- DefaultHandler
DefaultHandler 實(shí)現(xiàn)了 ContentHandler, ErrorHandler, DTDHandler, 和EntityResolver 接口 (當(dāng)然其中有一些null方法), 如果你感興趣的話,你可以在你的程序中重載它。
- ContentHandler
當(dāng)讀到XML的tag時(shí),就會(huì)調(diào)用到這個(gè)接口中的startDocument, endDocument, startElement, 和 endElement 方法。同時(shí),這個(gè)接口還定義了characters 和processingInstruction,方法,分別地,當(dāng)parser遇到XML的element或者inline processing instruction的時(shí)候調(diào)用。
- ErrorHandler
當(dāng)遇到不同類(lèi)型的錯(cuò)誤的時(shí)候分別調(diào)用相應(yīng)的"錯(cuò)誤"方法,這些方法包括:error,fatalError和warning。
- DTDHandler
該接口所定義的方法只用在處理DTD信息的時(shí)候。
- EntityResolver
給接口中的resolveEntity方法只在parser遇到URI標(biāo)識(shí)數(shù)據(jù)的時(shí)候才調(diào)用。
更詳細(xì)地api介紹,請(qǐng)參看SAX的官方API文檔。 例子: 在我們這個(gè)例子中,我們處理一個(gè)xml文件,然后將其值set到對(duì)象中。這是一個(gè)非常常用的使用情況。以下就是我們需要處理的xml文件。 Test.xml <?xml version="1.0" ?> <customers> <customer> <id>#001</id> <name>Micke</name> <address>Najing</address> </customer> <customer> <id>#002</id> <name>Car</name> <address>Suzhou</address> </customer> <customer> <id>#003</id> <name>Jimmy</name> <address>ChengDu</address> </customer> <customer> <id>#004</id> <name>Henry</name> <address>Xi‘a(chǎn)n</address> </customer> </customers> 這是一個(gè)非常簡(jiǎn)單的xml文件,customers中間有數(shù)個(gè)customer,每一個(gè)customer中包含三個(gè)屬性id, name, address。 根據(jù)這個(gè)xml文件,我們將Date Object設(shè)置如下。 /* * Customers.java * Create @ 2004-4-27 22:04:45 * by Jiabo */ import java.util.*; /** * Customers * Create @ 2004-4-27 22:04:45 * by Jiabo */ public class Customers { private Vector customers; public Customers() { customers = new Vector(); } public void addCustomer(Customer customer) { customers.add(customer); } public String toString() { String newline = System.getProperty("line.separator"); StringBuffer buf = new StringBuffer(); for (int i = 0; i < customers.size(); i++) { buf.append(customers.elementAt(i)).append(newline); } return buf.toString(); } } class Customer { private String id; private String name; private String address; /** * @return */ public String getAddress() { return address; } /** * @return */ public String getId() { return id; } /** * @return */ public String getName() { return name; } /** * @param string */ public void setAddress(String string) { address = string; } /** * @param string */ public void setId(String string) { id = string; } /** * @param string */ public void setName(String string) { name = string; } public String toString(){ return "Customer: ID=‘" + id + "‘ Name=‘" + name + "‘ Address=‘" + address + "‘"; } } 接下來(lái)是xml的處理器。 /* * Test.java * Created on 2004-4-10 * by Jiabo */ import java.util.*; import org.xml.sax.*; import org.xml.sax.helpers.DefaultHandler; /** * Test * Create on 2004-4-10 19:20:27 * by Jiabo */ public class Unmarshaller extends DefaultHandler { private Customers customers; private Stack stack; private boolean isStackReadyForText; private Locator locator; /** * init */ public Unmarshaller() { stack = new Stack(); isStackReadyForText = false; } /** * @return customers */ public Customers getCustomers() { return customers; } /** * callbacks */ public void setDocumentLocator(Locator rhs) { locator = rhs; } //========================================== // SAX DocumentHandler methods //========================================== public void startElement( String uri, String sName, String qName, Attributes attrs) { isStackReadyForText = false; if (sName.equals("customers")) { stack.push(new Customers()); } else if (sName.equals("customer")) { stack.push(new Customer()); } else if ( sName.equals("id") || sName.equals("name") || sName.equals("address")) { stack.push(new StringBuffer()); isStackReadyForText = true; } else { } } public void endElement(String namespaceURI, String sName, String qName){ isStackReadyForText = false; Object temp = stack.pop(); if (sName.equals("customers")) { customers = (Customers) temp; } else if (sName.equals("customer")) { ((Customers) stack.peek()).addCustomer((Customer) temp); } else if (sName.equals("id")) { ((Customer) stack.peek()).setId(temp.toString()); } else if (sName.equals("name")) { ((Customer) stack.peek()).setName(temp.toString()); } else if (sName.equals("address")) { ((Customer) stack.peek()).setAddress(temp.toString()); } } public void characters(char[] data, int start, int length) { if (isStackReadyForText == true) { ((StringBuffer) stack.peek()).append(data, start, length); } else { } } } 在這里我們處理xml文件的思路非常簡(jiǎn)單,就是使用一個(gè)棧,遇到"<"表示element的開(kāi)始,然后就看與我們既定的Data Object的名字是否相符合,符合就new一個(gè)該對(duì)象,并將其壓棧;不符合就什么都不做,sax的處理框架就會(huì)自己去處理下一個(gè)element。而當(dāng)遇到"/>"的時(shí)候我們還是看的他名字與DataObject的名字是否相符,相符合的話就出棧,然后set進(jìn)對(duì)象里面。如此循環(huán),就處理完了我們上面那個(gè)簡(jiǎn)單得xml文件。 我們需要做的事情就只有這些。其他如何處理的,handler回自己調(diào)用相應(yīng)的startElement,endElement等方法去處理。 以下是程序的入口: /* * main.java * Create @ 2004-4-27 22:18:41 * by Jiabo */ import java.io.*; import javax.xml.parsers.*; import org.xml.sax.*; /** * main * Create @ 2004-4-27 22:18:41 * by Jiabo */ public class Main { public static void main(String args[]) { Customers customers = null; if (args.length != 1) { System.err.println("Usage: cmd filename"); System.exit(1); } try { Unmarshaller handler = new Unmarshaller(); SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser saxParser = factory.newSAXParser(); File file = new File(args[0]); InputSource src = new InputSource(new FileInputStream(file)); saxParser.parse( src ,handler); customers = handler.getCustomers(); } catch (Throwable t) { t.printStackTrace(); } System.out.println(customers); } } 如前面所述,通過(guò)一個(gè)工廠方法得到一個(gè)SAXParser的實(shí)例,然后就可以編譯這個(gè)xml文件了。這樣你就可以得到如下結(jié)果: Customer: ID =‘#001‘ Name=‘Micke‘ Address=‘Najing‘ Customer: ID =‘#002‘ Name=‘Car‘ Address=‘Suzhou‘ Customer: ID =‘#003‘ Name=‘Jimmy‘ Address=‘ChengDu‘ Customer: ID =‘#004‘ Name=‘Henry‘ Address=‘Xi‘a(chǎn)n‘ Sax的系統(tǒng)框架中還有其他得好些方法,讀者不妨試試他們是如何使用的,這對(duì)以后實(shí)戰(zhàn)處理xml文件會(huì)有很大的方便。 DOM DOM的框架輪廓 DOM的API概述 一般而言,我們使用javax.xml.parsers.DocumentBuilderFactory來(lái)得到DocumentBuilder的一個(gè)實(shí)例。當(dāng)然你也可以DocumentBuilder newDocument()方法來(lái)得到一個(gè)實(shí)現(xiàn)了org.w3c.dom.Document接口的空的Document對(duì)象。 - DocumentBuilderFactory
它可以根據(jù)系統(tǒng)屬性生成一個(gè)builder實(shí)例。
- DocumentBuilder
用于處理生成Document。 更詳細(xì)地api介紹,請(qǐng)參看DOM的官方API文檔。 所以我們可以簡(jiǎn)單地這樣: DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse("test.xml"); 就可以出得到一個(gè)Document。 實(shí)例: 我們依然處理test.xml。和SAX一樣,也需要有paser。其實(shí)思路是非常簡(jiǎn)單而明晰的,上面我們已經(jīng)說(shuō)過(guò),DOM是將所有的xml讀入內(nèi)存,以樹(shù)的結(jié)構(gòu)來(lái)處理的,所以呢,對(duì)節(jié)點(diǎn)的分析就是解決問(wèn)題的關(guān)鍵,如下。 代碼如下: /* * Test.java * Created on 2004-4-10 * by Jiabo */ import org.w3c.dom.*; /** * Test * Create on 2004-4-10 19:20:27 * by Jiabo */ public class Unmarshaller { public Unmarshaller() { } public Customers UnmarshallCustomers(Node rootNode) { Customers customers = new Customers(); Node n; NodeList nodes = rootNode.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { n = nodes.item(i); if (n.getNodeType() == Node.ELEMENT_NODE) { if ("customer".equals(n.getNodeName())) { customers.addCustomer(UnmarshallCustomer(n)); } else { } } } return customers; } public Customer UnmarshallCustomer(Node customerNode) { Customer customer = new Customer(); Node n; NodeList nodes = customerNode.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { n = nodes.item(i); if ("id".equals(n.getNodeName())) { customer.setId(UnmarshallText(n)); } else if ("name".equals(n.getNodeName())) { customer.setName(UnmarshallText(n)); } else if ("address".equals(n.getNodeName())) { customer.setAddress(UnmarshallText(n)); } } return customer; } public String UnmarshallText(Node textNode) { StringBuffer buf = new StringBuffer(); Node n; NodeList nodes = textNode.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { n = nodes.item(i); if (n.getNodeType() == Node.TEXT_NODE) { buf.append(n.getNodeValue()); } else { } } return buf.toString(); } } 下面是如何驅(qū)動(dòng)DOM去處理xml文件部分。還是先得到一個(gè)DocumentBuilderFactory工廠,在用他生成一個(gè)DocumentBuilder一個(gè)實(shí)例,在調(diào)用parse方法就可以分析這個(gè)xml文件了。 /* * main.java * Create @ 2004-4-27 22:18:41 * by Jiabo */ import java.io.*; import org.w3c.dom.*; import javax.xml.parsers.*; /** * main * Create @ 2004-4-27 22:18:41 * by Jiabo */ public class Main { public static void main(String args[]) { Customers customers = null; Document doc = null; if (args.length != 1) { System.err.println("Usage: cmd filename"); System.exit(1); } try { Unmarshaller handler = new Unmarshaller(); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); doc = builder.parse( new File(args[0]) ); customers = handler.UnmarshallCustomers(doc.getDocumentElement()); } catch (Throwable t) { t.printStackTrace(); } System.out.println(customers); } } 總結(jié): 這里是對(duì)xml處理的一個(gè)簡(jiǎn)介,力求簡(jiǎn)介,明了,以最快的速度幫助讀者入門(mén),所以,沒(méi)有完整地使用庫(kù)中的方法。 Xml文件的處理,對(duì)于webservice是基礎(chǔ)的基礎(chǔ)。而SAX和DOM又是xml處理中基礎(chǔ)的基礎(chǔ)。濁文請(qǐng)讀者笑納。 參考: http://java.sun.com/xml/jaxp/docs.html |