XML的解析(DOM以及SAX方式)
感谢http://blog.csdn.net/redarmy_chen/article/details/12951649(关于SAX解析)以及http://blog.csdn.net/zhangerqing/article/details/8253532(DOM解析)。首先申明本文并非个人原创,而是整合的前面两篇文章,供大家以及自己参考。
1.XML
XML是一种对独立于任何编程语言的数据进行编码的机制。主要用于数据存储,数据传输以及配置文件(Ioc)等方面。XML要求所有的标记必须成对出现,且区分大小写。举个例子:
<bookstore> <book category="COOKING"> <title lang="en">Everyday Italian</title> <author>Giada De Laurentiis</author> <year>2005</year> <price>30.00</price> </book> <book category="CHILDREN"> <title lang="en">Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29.99</price> </book> <book category="WEB"> <title lang="en">Learning XML</title> <author>Erik T. Ray</author> <year>2003</year> <price>39.95</price> </book> </bookstore>
可以从这个例子,得出XML的结构,简单说XML的结构即为多叉树结构。上面代码的结构图如下:
2.XML的解析技术
DOM
DOM 是 W3C 处理 XML 的标准 API,它是许多其它与 XML 处理相关的标准的基础,不仅是 Java,其它诸如 Javascript,PHP,MS .NET 等等语言都实现了该标准, 成为了应用最为广泛的 XML 处理方式。当然,为了能提供更多更加强大的功能,Java 对于 DOM 直接扩展工具类有很多,比如很多 Java 程序员耳熟能详的 JDOM,DOM4J 等等, 它们基本上属于对 DOM 接口功能的扩充,保留了很多 DOM API 的特性。其处理方式是将 XML 整个作为类似树结构的方式读入内存中以便操作及解析,因此支持应用程序对 XML 数据的内容和结构进行修改,但是同时由于其需要在处理开始时将整个 XML 文件读入到内存中去进行分析,因此其在解析大数据量的 XML 文件时会遇到类似于内存泄露以及程序崩溃的风险。例子如下:
首先造一个xml文档出来:(books.xml放在项目根路径下,不是src,可以在工程文件所在文件夹根目录下先创建txt文档,编辑books内容,然后保存为xml格式即可)
<?xml version="1.0" encoding="UTF-8"?> <books> <book id="01" name="book1"> <title>Harry Potter</title> <author>J K. Rowling</author> </book> <book id="02" name="book2"> <title>Thinking in Java</title> <author>Bruke</author> </book> </books>
DOM 解析:
import java.io.File; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class DOMTest { /* build a DocumentBuilderFactory */ DocumentBuilderFactory builderFactory = DocumentBuilderFactory .newInstance(); public static void main(String[] args) { DOMTest parser = new DOMTest(); Document document = parser.parse("books.xml"); /* get root element */ Element rootElement = document.getDocumentElement(); /* get all the nodes whose name is book */ NodeList nodeList = rootElement.getElementsByTagName("book"); if (nodeList != null) { for (int i = 0; i < nodeList.getLength(); i++) { /* get every node */ Node node = nodeList.item(i); /* get the next lever's ChildNodes */ NodeList nodeList2 = node.getChildNodes(); for (int j = 0; j < nodeList2.getLength(); j++) { Node node2 = nodeList2.item(j); if (node2.hasChildNodes()) { System.out.println(node2.getNodeName() + ":" + node2.getFirstChild().getNodeValue()); } } } } } /* Load and parse XML file into DOM */ public Document parse(String filePath) { Document document = null; try { /* DOM parser instance */ DocumentBuilder builder = builderFactory.newDocumentBuilder(); /* parse an XML file into a DOM tree */ document = builder.parse(new File(filePath)); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return document; } }
输出:
title:Harry Potter
author:J K. Rowling
title:Thinking in Java
author:Bruke
SAX:
SAX解析XML文件采用事件驱动的方式进行,也就是说,SAX是逐行扫描文件,遇到符合条件的设定条件后就会触发特定的事件,回调你写好的事件处理程序。使用SAX的优势在于其解析速度较快,相对于DOM而言占用内存较少。而且SAX在解析文件的过程中得到自己需要的信息后可以随时终止解析,并不一定要等文件全部解析完毕。凡事有利必有弊,其劣势在于SAX采用的是流式处理方式,当遇到某个标签的时候,它并不会记录下以前所遇到的标签,也就是说,在处理某个标签的时候,比如在startElement方法中,所能够得到的信息就是标签的名字和属性,至于标签内部的嵌套结构,上层标签、下层标签以及其兄弟节点的名称等等与其结构相关的信息都是不得而知的。实际上就是把XML文件的结构信息丢掉了,如果需要得到这些信息的话,只能你自己在程序里进行处理了。
SAX采用事件处理的方式解析XML文件,利用 SAX 解析 XML 文档,涉及两个部分:解析器和事件处理器:
解析器可以使用JAXP的API创建,创建出SAX解析器后,就可以指定解析器去解析某个XML文档。
解析器采用SAX方式在解析某个XML文档时,它只要解析到XML文档的一个组成部分,都会去调用事件处理器的一个方法,解析器在调用事件处理器的方法时,会把当前解析到的xml文件内容作为方法的参数传递给事件处理器。
事件处理器由程序员编写,程序员通过事件处理器中方法的参数,就可以很轻松地得到sax解析器解析到的数据,从而可以决定如何对数据进行处理。
以SAX的ContentHandler接口为例,了解常用方法:startElement、endElement、characters等:
startElement方法说明:
void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException 方法说明: 解析器在 XML 文档中的每个元素的开始调用此方法;对于每个 startElement 事件都将有相应的 endElement 事件(即使该元素为空时)。所有元素的内容都将在相应的 endElement 事件之前顺序地报告。 参数说明: uri - 名称空间 URI,如果元素没有名称空间 URI,或者未执行名称空间处理,则为空字符串 localName - 本地名称(不带前缀),如果未执行名称空间处理,则为空字符串 qName - 限定名(带有前缀),如果限定名不可用,则为空字符串 atts - 连接到元素上的属性。如果没有属性,则它将是空 Attributes 对象。在 startElement 返回后,此对象的值是未定义的
endElement方法说明:
void endElement(String uri, String localName, String qName) throws SAXException接收元素结束的通知。 SAX 解析器会在 XML 文档中每个元素的末尾调用此方法;对于每个 endElement 事件都将有相应的 startElement 事件(即使该元素为空时)。 参数: uri - 名称空间 URI,如果元素没有名称空间 URI,或者未执行名称空间处理,则为空字符串 localName - 本地名称(不带前缀),如果未执行名称空间处理,则为空字符串 qName - 限定的 XML 名称(带前缀),如果限定名不可用,则为空字符串
characters方法:
void characters(char[] ch, int start, int length) throws SAXException 接收字符数据的通知,可以通过new String(ch,start,length)构造器,创建解析出来的字符串文本. 参数: ch - 来自 XML 文档的字符 start - 数组中的开始位置 length - 从数组中读取的字符的个数
下面我们就具体讲解sax解析的操作:
一.我们通过XMLReaderFactory、XMLReader完成,步骤如下
1.通过XMLReaderFactory创建XMLReader对象 XMLReader reader = XMLReaderFactory.createXMLReader(); 2. 设置事件处理器对象 reader.setContentHandler(new MyDefaultHandler()); 3.读取要解析的xml文件 FileReader fileReader =new FileReader(new File("src\\sax\\startelement\\web.xml")); 4.指定解析的xml文件 reader.parse(new InputSource(fileReader));
案例:通过案例对uri、localName、qName和attribute参数有更加深入的了解
1.首先创建要解析的web.xml文件,内容如下
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns:csdn="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <csdn:display-name></csdn:display-name> </web-app> <!-- uri - 名称空间 URI,如果元素没有任何名称空间 URI,或者没有正在执行名称空间处理,则为空字符串。 xml namespace-xmlns localName - 本地名称(不带前缀),如果没有正在执行名称空间处理,则为空字符串。 qName - 限定的名称(带有前缀),如果限定的名称不可用,则为空字符串。 attributes - 附加到元素的属性。如果没有属性,则它将是空的 Attributes 对象。 -->
2.创建解析测试类及事件处理的内部类代码如下
package sax.startelement; import java.io.File; import java.io.FileReader; import org.junit.Test; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.XMLReaderFactory; public class Demo3 { @Test public void test() throws Exception { // 通过XMLReaderFactory创建XMLReader对象 XMLReader reader = XMLReaderFactory.createXMLReader(); // 设置事件处理器对象 reader.setContentHandler(new MyDefaultHandler()); // 读取要解析的xml文件 FileReader fileReader = new FileReader(new File( "src\\sax\\startelement\\web.xml")); // 指定解析的xml文件 reader.parse(new InputSource(fileReader)); } // 自定义的解析类,通过此类中的startElement了解uri,localName,qName,Attributes的含义 class MyDefaultHandler extends DefaultHandler { @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { super.startElement(uri, localName, qName, attributes); System.out .println("--------------startElement开始执行--------------------------"); System.out.println("uri:::" + uri); System.out.println("localName:::" + localName); System.out.println("qName:::" + qName); for (int i = 0; i < attributes.getLength(); i++) { String value = attributes.getValue(i);// 获取属性的value值 System.out.println(attributes.getQName(i) + "-----" + value); } System.out .println("------------------startElement执行完毕---------------------------"); } } }
3.程序运行的结果如下:
二.我们通过SAXParserFactory、SAXParser、XMLReader完成,步骤如下
1.使用SAXParserFactory创建SAX解析工厂
SAXParserFactory spf = SAXParserFactory.newInstance();
2.通过SAX解析工厂得到解析器对象
SAXParser sp = spf.newSAXParser();
3.通过解析器对象得到一个XML的读取器
XMLReader xmlReader = sp.getXMLReader();
4.设置读取器的事件处理器
xmlReader.setContentHandler(new BookParserHandler());
5.解析xml文件
xmlReader.parse("book.xml");
说明:如果只是使用SAXParserFactory、SAXParser他们完成只需要如下3步骤
1.获取sax解析器的工厂对象
SAXParserFactory factory = SAXParserFactory.newInstance();
2.通过工厂对象 SAXParser创建解析器对象
SAXParser saxParser = factory.newSAXParser();
3.通过解析saxParser的parse()方法设定解析的文件和自己定义的事件处理器对象
saxParser.parse(new File("src//sax//sida.xml"), new MyDefaultHandler());
案例:解析出"作者"元素标签中的文本内容
1.需要解析的sida.xml文件
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE 四大名著[ <!ELEMENT 四大名著 (西游记,红楼梦)> <!ATTLIST 西游记 id ID #IMPLIED> ]> <四大名著> <西游记 id="x001"> <作者>吴承恩</作者> </西游记> <红楼梦 id="x002"> <作者>曹雪芹</作者> </红楼梦> </四大名著>
2.解析测试类和事件处理器类的实现代码
package sax; import java.io.File; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.junit.Test; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; public class SaxTest { @Test public void test() throws Exception { // 1.获取sax解析器的工厂对象 SAXParserFactory factory = SAXParserFactory.newInstance(); // 2.通过工厂对象 SAXParser创建解析器对象 SAXParser saxParser = factory.newSAXParser(); // 3.通过解析saxParser的parse()方法设定解析的文件和自己定义的事件处理器对象 saxParser.parse(new File("src//sax//sida.xml"), new MyDefaultHandler()); } // 自己定义的事件处理器 class MyDefaultHandler extends DefaultHandler { // 解析标签开始及结束的的标识符 boolean isOk = false; @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { super.startElement(uri, localName, qName, attributes); // 当解析作者元素开始的时候,设置isOK为true if ("作者".equals(qName)) { isOk = true; } } @Override public void characters(char[] ch, int start, int length) throws SAXException { // TODO Auto-generated method stub super.characters(ch, start, length); // 当解析的标识符为true时,打印元素的内容 if (isOk) { System.out.println(new String(ch, start, length)); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { super.endElement(uri, localName, qName); // 当解析作者元素的结束的时候,设置isOK为false if ("作者".equals(qName)) { isOk = false; } } } }
最后可以看到输出结果为吴承恩、曹雪芹