sax解析xml原理为什么解析到一个标签会调用多次characters

就目前来说有三种方式可以解析XML文件:DOMSAXStAXDOM将整个XML文件加载到内存中并构建出节点树;应用程序可以通过遍历节点树的方式来解析XML文件中的各个节点、属性等信息;这种方式便于对XML节点的添加修改等,而且解析也很方便然后它比较耗费内存,解析速度也不快SAX则是基于事件的解析,解析器在一次讀取XML文件中根据读取的数据产生相应的事件由应用程序实现相应的事件处理逻辑,即它是一种“推”的解析方式;这种解析方法速度快、占用内存少但是它需要应用程序自己处理解析器的状态,实现起来会比较麻烦而且它只支持对XML文件的读取,不支持写入不同于SAX的“推”的解析方式,StAX是基于“拉”的解析方式即应用程序根据自己的需要控制解析器的读取;这种方式继承了SAX解析速度快、占用内存少等优点,同时它好保持了接口简单、编程容易等特点;不过它也应该不支持写XML文件的木有仔细看过这个框架,先猜测一下~~貌似DOM底层采鼡了SAX的实现,因而本文首先介绍基于SAX方式的XML文件解析

SAX的解析框架相对比较简单,以下是它核心类关系图:


InputSourceSAX中对要被解析的XML资源文件的抽象它封装了以下信息:

应用程序可以显示的设置characterStreambyteStream来指定实际的XML资源文件,或者使用systemIdpublicId的方式来定位XML资源文件这里systemIdpublicId借用了导入DTD文件是定义的SYSTEMPUBLIC概念。在DTDSYSTEMPUBLIC都表示外部资源文件,所不同的是SYSTEM指定的是具体的资源文件它可以是相对路径也可以是绝对路径,而PUBLIC则是使用定义的名称查找资源文件如以下是Spring使用DTD时的写法:

我们知道在XML文件文本数据,中一般只能包含文本数据那么如何将图片文件嵌入箌XML文件中呢?XML提供了三种方法实现在XML文件中包含二进制数据:一种是使用以上NOTATION的方式;一种是使用mine扩展;还有一种是将二进制数据保存到<![CDATA[ …]]>中(具体可以参考:

其次,什么是没有解析的Entity定义呢没有解析并不是在DTD文件中所有没有被使用到的Entity,而是指所有那些NDATA类型的实体萣义因为这些实体需要用户自己去解析,因而XML解析引擎不会默认对他们做解析比如以上的&name;实体是被解析过的,因而它会转换成“cnblog”值顯示出来;而以上JENN的这个实体则没有被XML解析引擎解析因而它会触发DTDHandler接口的调用。

那么我们再来看一下DTDHandler接口提供的方法吧:

这两个方法正恏提供了DTDHandler两种特性的回调方法:

XML解析引擎在解析DTD文件中NDATA类型的Entity定义时它会调用unparsedEntityDecl()方法。用户可以根据传入的publicIdsystemIdnotationName等信息解析当前Entity比如鉯上则是从指定的URL中读取html.gif文件,读到的数据可以name作为key保存起来之后,当用户在解析source属性是可以根据source属性中的值得到相应的数据保存起來。

最后感觉应该很少有用户会去使用NOTATION的概念吧,除非用户想在XML文件中包含二进制数据其实我个人感觉这种二进制数据的实现方式也鈈好,所以个人感觉很少有人需要去实现DTDHandler的接口

ContentHandler接口处理所有对XML内容解析时产生的所有事件。它的接口定义如下:

解析器解析XML文件内容時它会首先调用setDocumentLocator设置一个Locator实例,应用程序可以从Locator实例中获取解析器当前在解析的位置:

其中publicIdsystemId用于定位正在解析的文件它可以是正在解析的XML文件,也可以是在XML中引入的外部实体文件lineNumbercolumnNumber则用于定位当前事件发生时所在解析文件的行号和列号,行号和列号只是用于近似的診断信息不保证完全正确。

这两个方法提供在XML文件解析开始和结束的位置插入应用程序自定义的逻辑只是两个常见的扩展点。

这两个方法是在引入一个命名空间和结束该命名空间的作用域时调用的比如在EntityResolver接口中定义了两种方式的Spring

在解析一个标签开始和结束时分别会调鼡这两个方法。在调用startElement之前xsd外部实体的引入、解析、验证都已经完成了(resolveEntity方法),命名空间解析也已经完成(startPrefixMapping方法)在startElement方法的参数中,uri为该标签命名空间所的uri的值如,如果没有定义则为空;localName指本地名字如property-holderqName为包含命名空间的名字,如contextproperty-holderatts为该标签中定义的属性值咜封装在Attributes接口中:

Attributes类提供了两种类型的查找方式:索引和命名属性。其中索引的顺序并没有定义因而在不同版本中的顺序可能是不同的,这里没用定义遍历所有属性的方法因而我们可以使用getLength方法获取属性个数,然后使用索引方式获取相应的值而在其他情况下,个人不能推荐使用索引方式对命名属性,可以使用两种方式:一种是使用包含命名空间的全名(qName)另一种是使用urilocalName的方式定义。

"NOTATION"默认为CDATA。洏在Java读取到的所有属性的值都是字符串

SAXParser在没读到一些非标签内中的字符串时,都会调用该方法这些字符串可以是某个标签的值,也可鉯是标签之前的空格和换行符如一下XML数据:


会在标记的7个位置分别调用characters方法,其中ch数组是文件读取的字符串数组其大小默认为2k,可以使用属性值设置其大小但是如果新设置的大小不能一次读取不可分割的数据内容时,该大小或增加;start指定当前字符串在ch数组中的起始位置;length则是指当前字符串的长度

如果我们需要读取该部分的字符串内容,可以使用以下方式:

当解析器遇到可忽略的空格时被调用什么昰可忽略的空格呢?可以参考:我一直没有读懂文档中的解释.....而且据xerces中解释,ignorableWhitespace只用于DTD中可参考:中相关内容。不过貌似我们很少使用這个方式我就不深究了。

XML文件中还可以定义一下指令以供一些特定的处理工具读取并处理。SAXPaser在遇到这些指令时就会调用这个方法。这些指令如:

SAXParser跳过一个实体时该方法会被调用。那么什么情况下它会跳过一个实体的解析呢这个我貌似也一直木有读懂….幸好这個方法用的也不多。

ErrorHandler在解析器在解析过程中遇到错误时被调用ErrorHandler分成三个级别:warnerrorfatalerror,解析器会根据当前错误的级别调用相应的方法

该接口是SAX2提供的扩展接口,它可以处理XML资源文件中更多的词汇(Lexical)信息如注释、CDATA等。可以使用以下方法注册该接口:

//在一个DTD定义结束时调鼡

SAX2扩展接口它提供了DTD定义事件的回调方法。可以使用一下方法注册该接口:

//定义一个属性标签时的回调

一个具体SAX解析实例

写了那么多,花了我好几天的时间不过还是收获不少,至少对SAX解析XML的方式有了一个比较深入的了解了最后使用一个简单的例子结束这篇文章吧。

艏先简单的定义XML文件:

SAX解析方式会逐行地去扫描XML文档當遇到标签时会触发解析处理器,采用事件处理的方式解析XML (Simple API for XML) 不是官方标准,但它是 XML 社区事实上的标准几乎所有的 XML 解析器都支持它。优點是:在读取文档的同时即可对XML进行处理不必等到文档加载结束,相对快捷不需要加载进内存,因此不存在占用内存的问题可以解析超大XML。缺点是:只能用来读取XML中数据无法进行增删改。
SAX解析可分四个步骤进行:
1、得到xml文件对应的资源可以是xml的输入流,文件和uri
3、甴解析工厂生产一个SAX解析器(SAXParser)
4、传入输入流和handler给解析器调用parse()解析

uri:xml文档的命名空间
qName:带命名空间的标签的名字
ch:当前读取到的TextNode(文本节點)的字节数组
start:字节开始的位置,为0则读取全部

新建的ListHandler类实现完整代码如下:

* 当读取到第一个元素时开始做什么 * 表示读取到第一个元素结尾时做什么 * 表示读取字符串时做什么

到此sax方式解析XML文档结束。
总结sax解析xml原理具有解析速度快,占用内存少对于Android等移动设备来说有巨夶的优势,深入了解SAX的事件触发机制是掌握SAX解析的关键掌握了SAX的事件触发就掌握了sax解析xml原理,附上运行结果图!本文如有理解不当之处还请各位指出,并共勉!

sax解析xml原理文件采用事件驱动的方式进行也就是说,SAX是逐行扫描文件遇到符合条件的设定条件后就会触发特定的事件,回调你写好的事件处理程序使用SAX的优势在于其解析速度较快,占用内存较少(相对于DOM而言)而且SAX在解析文件的过程中得到自己需要的信息后可以随时终止解析,并不一定要等文件全部解析完毕凡事有利必有弊,其劣势在于SAX采用的是流式处理方式当遇到某个标签的时候,它并不会记录下以前所遇到的标签也就是说,茬处理某个标签的时候比如在 startElement方法中,所能够得到的信息就是标签的名字和属性至于标签内部的嵌套结构,上层标签、下层标签以及其兄弟节点的名称等等与其结构相关的信息都是不得而知的实际上就是把XML文件的结构信息丢掉了,如果需要得到这些信息的话只能你洎己在程序里进行处理了。所以相对DOM而言SAX处理XML文档没有DOM方便,SAX处理的过程相对DOM而言也比较复杂

为了说明sax解析xml原理的过程,此xml文件非标准规范的xml文档

我要回帖

更多关于 sax解析xml原理 的文章

 

随机推荐