浅谈XML解析

一.XML

XML是一种可扩张标记语言,广泛用来标记数据、定义数据类型,是一种允许用户对自己的标 记语言进行定义的源语言.符合W3C标准.XML是无作为的,不会做任何事情,只用于数据的描述,储存和传输.同时XML也是无所不在的,在Web中的作用不亚于HTML,XML 是各种应用程序之间进行数据传输的最常用的工具.

二.推模型和拉模型.

推模型是当消息来之时,把所有相关信息都通过参数的形式"推给"观察者.事件驱动编程(Event-driven programming),基于回调(callback)机制,也就是推模型,即使不需要也把数据推给你.拉模型是当通知消息来之时,通知的函数不带任何相关的信息,而是要观察者主动去“拉”信息,通过轮询(loop check)实现就是拉模型,给你一个引用,数据在拉之前全部准备好了,需要是拉需要.

三.XML处理方式

主要有两种:基于树和基于事件驱动的.很多XML解析器,包括MSXML的最新版本对两种模型都支持.基于树的解析器通常称为DOM(document object model 文档对象模型)解析器,而基于事件驱动的解析器通常被称为SAX(simple API for XML 简单应用编程接口)解析器.二者都是以它们支持的规则命名的.

DOM是W3C推荐的XML文档应用接口(API)的标准.所有的程序都可以这个API来操作XML,读取信息,添加节点以及编辑当前的内容.而SAX并不是W3C的推荐标准,但也得到了大大小小软件公司的支持.基于SAX的解析器顺序地读取XML文本,当它遇到文档中的重要部分时就触发事件,比如在元素的起始或结束的位置.

两种处理模型都有优点.DOM的解析提供了对XML文档的完整的读写访问,并且可以通过遍历文档树对文档内的节点进行访问.还可以对照DTD或XML模式来判断一个文档是否有效,然而,DOM的解析必须将XML文档整个读入内存中,所以当遇到较大的XML文档时,DOM解析就会变得较慢而且比较消耗内存.很难准确判断何种XML文档才是过大的,因为处理时间会根据能力,内存,可用时间以及是否在单用户或多用户环境下工作而变化.一般而言,大多数系统可以处理大至几十兆的文件,但是对于更大的文件就需要十分小心了.另一方面,SAX的模型是串行操作的.一个节点被处理后就被丢弃了,并且不会再被处理.整个文档并不一次性的读入内存中,这样就避免了由较大的XML文档而引起的处理问题.这种处理方法将由用户负责保存XML文档中的可供后续使用的信息.

举个例子,对于一个通信系统的中间路由程序来说,SAX是理想的.一个读入的XML文档通常只有一个很小的路由首部信息,但却有需要发送到终点的较大文档.使用SAX模型时,路由设备可以仅读取路由信息而忽略掉整个文档,因为文档与传递并无关系.然而,DOM的解析器却需要在读取整个文档后才可以将之传至终点.

四.DOM解析.

DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准.DOM是以层次结构组织的节点或信息片断的集合.这个层次结构允许开发人员在树中寻找 特定信息.分析该结构通常需要加载整个文档和构造层次结构,然后才能做任何工作.由于它是基于信息层次的,因而DOM被认为是基于树或基于对象的.DOM解析器把XML文档转化为一个包含其内容的树,并可以对树进行遍历.DOM是拉模型,在遍历文档时,会把感兴趣的部分从读取器中拉出,不需要引发事件,允许我们选择性地处理节点.这大大提高了灵活性,以及整体效率.

public static void domParse() throws Exception {
File file = 
new File("F:/test/web项目/workspace/xml/src/users.xml");
		//1、创建DocumentBuilder工厂
		DocumentBuilderFactory dbf = 
DocumentBuilderFactory.newInstance();
		//2、创建DocumentBuilder
		DocumentBuilder db = dbf.newDocumentBuilder();
		//3、将xml文件解析为Document对象,代表DOM树
		Document document = db.parse(file);
		//将文档标准化,去除无用的空格和空行,即删除无用的text node
		document.normalize();
		NodeList nodeList = document.getElementsByTagName("user");
		for(int i = 0; i < nodeList.getLength(); i++) {

			Element element = (Element) nodeList.item(i);
			System.out.println("======user start=======");
			String uuid = element.getElementsByTagName("uuid").item(0).getTextContent();
			System.out.println(uuid);
			String name = element.getElementsByTagName("name").item(0).getTextContent();
			System.out.println(name);
			System.out.println("======user end=======");
		}



DOM有5个基本对象:document,node,nodelist,element,attr.

1.document对象

代表了整个XML的文档,所有其它的Node,都以一定的顺序包含在Document对象之内,排列成一个树形的结构,程序员可以通过遍历这颗树来得到XML文档的所有的内容,这也是对XML文档操作的起点.我们总是先通过解析XML源文件而得到一个Document对象,然后再来执行后续的操作.此外,Document还包含了创建其它节点的方法,比如createAttribute()用来创建一个Attr对象.

2.node对象

DOM结构中最为基本的对象,代表了文档树中的一个抽象的节点.在实际使用的时候,很少会真正的用到Node这个对象,而是用到诸如Element、Attr、Text等Node对象的子对象来操作文档.Node对象为这些对象提供了一个抽象的、公共的根.虽然在Node对象中定义了对其子节点进行存取的方法,但是有一些Node子对象,比如Text对象,它并不存在子节点,这一点是要注意的.

3.nodelist对象

顾名思义,就是代表了一个包含了一个或者多个Node的列表.可以简单的把它看成一个Node的数组,我们可以通过方法来获得列表中的元素:getLength();返回列表的长度,item(int);返回指点位置的node对象

4.element对象

代表的是XML文档中的标签元素,继承于Node,亦是Node的最主要的子对象.在标签中可以包含有属性,因而Element对象中有存取其属性的方法.而任何Node中定义的方法,也可以用在Element对象上面.

5.attr对象

代表了某个标签中的属性.Attr继承于Node,但是因为Attr实际上是包含在Element中的,它并不能被看作是Element的子对象,因而在DOM中Attr并不是DOM树的一部分,所以Node中的getParentNode(),getPreviousSibling()和getNextSibling()返回的都将是null.也就是说,Attr其实是被看作包含它的Element对象的一部分,它并不作为DOM树中单独的一个节点出现.这一点在使用的时候要同其它的Node子对象相区别.

五.SAX解析

SAX是Simple API for XML的缩写,它并不是由W3C官方所提出的标准,可以说是“民间”的事实标准.实际上,它是一种社区性质的讨论产物.虽然如此,在XML中对SAX的应用丝毫不比DOM少,几乎所有的XML解析器都会支持它.

与DOM比较而言,SAX是一种轻量型的方法.我们知道,在处理DOM的时候,我们需要读入整个的XML文档,然后在内存中创建DOM树,生成DOM树上的每个Node对象.当文档比较小的时候,这不会造成什么问题,但是一旦文档大起来,处理DOM就会变得相当费时费力.特别是其对于内存的需求,也将是成倍的增长,以至于在某些应用中使用DOM是一件很不划算的事(比如在applet中).这时候,一个较好的替代解决方法就是SAX.

SAX在概念上与DOM完全不同.首先,不同于DOM的文档驱动,它是事件驱动的,也就是说,它并不需要读入整个文档,而文档的读入过程也就是SAX的解析过程.所谓事件驱动,是指一种基于回调(callback)机制的程序运行方法.(如果你对Java新的代理事件模型比较清楚的话,就会很容易理解这种机制了)

回调:由我们在组件中定义,而不由我们调用,由容器或框架调用

SAX是推模型,它是一种靠事件驱动的模型.当它每发现一个节点就引发一个事件,而我们需要编写这些事件的处理程序.这样的做法很麻烦,且不灵活.

import java.io.File;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;


public class SaxTest {
	public static void main(String[] args) throws Exception {
		saxParse();
	}

	private static void saxParse() throws Exception {
		
		File file = new File("F:/test/web项目/workspace/xml/src/users.xml");
		
		//1、创建SAXParserFactory 工厂
		SAXParserFactory spf = SAXParserFactory.newInstance();
		
		//2、创建SAXParser
		SAXParser sp = spf.newSAXParser();
		//3、解析XML文档,并回调处理器的相应事件
		sp.parse(file, new UserSaxHandler());
		
	}
	
}



六.StAX解析

针对于XML的流式API(StAX),是在2004年3月的JSR 173规范中引入,这是一种针对XML的流式拉分析API.StAX是JDK 6.0提供的一种新特征.

一个推模型分析器不断地生成事件,直到XML文档被完全分析结束.但是,拉分析由应用程序进行调整;因此,分析事件是由应用程序生成的.这意味着,使用StaX,你可以推迟分析-在分析时跳过元素并且分析多个文档.在使用DOM API的时候,你必须把整个的XML文档分析成一棵DOM结构,这样也就降低了分析效率.而借助于StAX,在分析XML文档时生成分析事件.

七.JDOM解析

JDOM的目的是成为Java特定文档模型,它简化与XML的交互并且比使用DOM实现更快.由于是第一个Java特定模型,JDOM一直得到大力推广和 促进.正在考虑通过“Java规范请求JSR-102”将它最终用作“Java标准扩展”.从2000年初就已经开始了JDOM开发.

JDOM与DOM主要有两方面不同.首先,JDOM仅使用具体类而不使用接口.这在某些方面简化了API,但是也限制了灵活性.第二,API大量使用了Collections类,简化了那些已经熟悉这些类的Java开发者的使用.

JDOM文档声明其目的是“使用20%(或更少)的精力解决80%(或更多)Java/XML问题”(根据学习曲线假定为20%).JDOM对于大多数 Java/XML应用程序来说当然是有用的,并且大多数开发者发现API比DOM容易理解得多.JDOM还包括对程序行为的相当广泛检查以防止用户做任何 在XML中无意义的事.然而,它仍需要您充分理解XML以便做一些超出基本的工作(或者甚至理解某些情况下的错误).这也许是比学习DOM或JDOM接口 都更有意义的工作.

JDOM自身不包含解析器.它通常使用SAX2解析器来解析和验证输入XML文档(尽管它还可以将以前构造的DOM表示作为输入).它包含一些转换器以将 JDOM表示输出成SAX2事件流、DOM模型或XML文本文档.JDOM是在Apache许可证变体下发布的开放源码.

八.DOM4J解析

虽然DOM4J代表了完全独立的开发结果,但最初,它是JDOM的一种智能分支.它合并了许多超出基本XML文档表示的功能,包括集成的XPath支持、 XML Schema支持以及用于大文档或流化文档的基于事件的处理.它还提供了构建文档表示的选项,它通过DOM4J API和标准DOM接口具有并行访问功能.从2000下半年开始,它就一直处于开发之中.
为支持所有这些功能,DOM4J使用接口和抽象基本类方法.DOM4J大量使用了API中的Collections类,但是在许多情况下,它还提供一些替 代方法以允许更好的性能或更直接的编码方法.直接好处是,虽然DOM4J付出了更复杂的API的代价,但是它提供了比JDOM大得多的灵活性.
    在添加灵活性、XPath集成和对大文档处理的目标时,DOM4J的目标与JDOM是一样的:针对Java开发者的易用性和直观操作.它还致力于成为比 JDOM更完整的解决方案,实现在本质上处理所有Java/XML问题的目标.在完成该目标时,它比JDOM更少强调防止不正确的应用程序行为.
    DOM4J是一个非常非常优秀的Java XML API,具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件.如今你可以看到越来越多的Java软件都在使用DOM4J来读写 XML,特别值得一提的是连Sun的JAXM也在用DOM4J.

九.比较

1.DOM:拉模型,把整个文档加载到内存中
        优点:整个文档树在内存中,便于操作;支持删除、修改、重新排列等多种功能;
        缺点:将整个文档调入内存(包括无用的节点),浪费时间和空间;
        使用场合:一旦解析了文档还需多次访问这些数据;硬件资源充足(内存、CPU)

2.SAX:推模型,事件驱动编程,基于回调SAX ,事件驱动.当解析器发现元素开始、元素结束、文本、文档的开始或结束等时,发送事件,程序员编写响应这些事件的代码,保存数据.
        优点:不用事先调入整个文档,占用资源少;
        缺点:不是持久的;事件过后,若没保存数据,那么数据就丢了;无状态性;从事件中只能得到文本,但不知该文本属于哪个元素;
        使用场合:数据量较大的XML文档,占用内存高,机器内存少,无法一次加载XML到内存;只需XML文档的少量内容,很少回头访问;
                         
3.JDOM :为减少DOM、SAX的编码量,出现了JDOM;
        优点:20-80原则,极大减少了代码量,提供常用API减少重复劳动
        使用场合:要实现的功能简单,如解析、创建等Java程序,但在底层,JDOM还是使用SAX(最常用)、DOM

十.性能比较

1.DOM4J性能最好,连Sun的JAXM也在用DOM4J.目前许多开源项目中大量采用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J来读取XML配置文件.如果不考虑可移植性,那就采用DOM4J.

2.JDOM和DOM在性能测试时表现不佳,在测试10M文档时内存溢出.在小文档情况下还值得考虑使用DOM和JDOM.虽然JDOM的开发者已经说明 他们期望在正式发行版前专注性能问题,但是从性能观点来看,它确实没有值得推荐之处.另外,DOM仍是一个非常好的选择.DOM实现广泛应用于多种编程语 言.它还是许多其它与XML相关的标准的基础,因为它正式获得W3C推荐(与基于非标准的Java模型相对),所以在某些类型的项目中可能也需要它(如在 javascript中使用DOM).

3.SAX表现较好,这要依赖于它特定的解析方式-事件驱动.一个SAX检测即将到来的XML流,但并没有载入到内存(当然当XML流被读入时,会有部分文档暂时隐藏在内存中)

将XML保存到文件中

private Document newDocument() throws ParserConfigurationException {
		//1、创建DocumentBuilder工厂
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		//2、创建DocumentBuilder
		DocumentBuilder db;
		db = dbf.newDocumentBuilder();
		
		Document document = db.newDocument();
		document.setXmlVersion("1.0");
		Element element = document.createElement("users");
		document.appendChild(element);
		return document;
	}
	
	private void createXmlFile(Document document) throws TransformerConfigurationException, TransformerException {
		//1、创建转换工程
		TransformerFactory tf = TransformerFactory.newInstance();
		//2、创建DOM源
		DOMSource ds = new DOMSource(document);
		//3、新建一个文件用于存储XML
		File file = new File(fileName);
		if(!file.exists()) {
			try {
				file.createNewFile();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		//将文件包装为结果
		StreamResult sr = new StreamResult(file);
		//转换
		tf.newTransformer().transform(ds,sr);
	}
	
	public void saveToXml() {
		Document document;
		try {
			document = newDocument();
			Element usersElement = 
(Element) document.getElementsByTagName("users").item(0);
			for(UserModel um : userList) {
				Element userElement = document.createElement("user");
				Element uuidElement = document.createElement("uuid");
				Element nameElement = document.createElement("name");
				usersElement.appendChild(userElement);
				userElement.appendChild(uuidElement);
				userElement.appendChild(nameElement);
				
				uuidElement.setTextContent(String.valueOf(um.getUuid()));
				nameElement.setTextContent(um.getName());	
			}
			createXmlFile(document);
		} catch (Exception e) {
			e.printStackTrace();
		}
			
	}

猜你喜欢

转载自mingnianshimanian.iteye.com/blog/2017054
今日推荐