XML 解析器

自由度 贡献于2012-09-03

作者 微软用户  创建于2010-11-14 05:51:00   修改者微软用户  修改于2010-11-14 05:51:00字数17081

文档摘要:SAX解析SAX是一个用于处理XML事件驱动的“推”模型,虽然它不是W3C标准,但它却是一个得到了广泛认可的API。SAX解析器不像DOM那样建立一个完整的文档树,而是在读取文档时激活一系列事件,这些事件被推给事件处理器,然后由事件处理器提供对文档内容的访问。
关键词:

SAX解析 SAX是一个用于处理XML事件驱动的“推”模型,虽然它不是W3C标准,但它却是一个得到了广泛认可的API。SAX解析器不像DOM那样建立一个完整的文档树,而是在读取文档时激活一系列事件,这些事件被推给事件处理器,然后由事件处理器提供对文档内容的访问。常见的事件处理器有三种基本类型: ● 用于访问XML DTD内容的DTDHandler; ● 用于低级访问解析错误的ErrorHandler; ● 用于访问文档内容的ContentHandler,这也是最普遍使用的事件处理器。 与DOM相比,SAX解析器能提供更好的性能优势,它提供对XML文档内容的有效低级访问。SAX模型最大的优点是内存消耗小,因为整个文档无需一次加载到内存中,这使SAX解析器可以解析大于系统内存的文档。另外,你无需像在DOM中那样为所有节点创建对象。最后,SAX“推”模型可用于广播环境,能够同时注册多个ContentHandler,并行接收事件,而不是在一个管道中一个接一个地进行处理。 SAX的缺点是你必须实现多个事件处理程序以便能够处理所有到来的事件,同时你还必须在应用程序代码中维护这个事件状态,因为SAX解析器不能交流元信息,如DOM的父/子支持,所以你必须跟踪解析器处在文档层次的哪个位置。如此一来,你的文档越复杂,你的应用逻辑就越复杂。虽然没有必要一次将整个文档加载到内存中,但SAX解析器仍然需要解析整个文档,这点和DOM一样。 也许SAX面临的最大问题是它没有内置如XPath所提供的那些导航支持。再加上它的单遍解析,使它不能支持随机访问。这一限制也表现在名字空间上: 对有继承名字空间的元素不做注解。这些限制使SAX很少被用于操作或修改文档。 那些只需要单遍读取内容的应用程序可以从SAX解析中大大受益。很多B2B和EAI应用程序将XML用做封装格式,接收端用这种格式简单地接收所有数据。这就是SAX明显优于DOM的地方:因高效而获得高吞吐率。在SAX 2.0 中有一个内置的过滤机制,可以很轻松地输出一个文档子集或进行简单的文档转换。   DOM解析器 1.DOM标准 DOM(Document Object Model,文档对象模型)是W3C制定的一套规范标准,即规定了解析文件的接口。各种语言可以按照DOM规范去实现这些接口,给出解析文件的解析器。DOM规范中所指的文件相当广泛,其中包括XML文件以及HTML文件。我们已经知道,接口只关心功能,但不规定功能的具体实现,因此,在特定语言中使用DOM规范就需要定义DOM规范指定的接口,并给出实现这些接口的类的集合,这一过程称做语言绑定。本章主要讲述DOM规范的Java语言绑定。Java语言通过把DOM规范中的接口用Java的接口(interface)写下来,并给出实现这些接口的类集合来实现DOM规范的Java语言绑定(Java language binding)。 DOM规范指定了语言绑定需要实现的接口,简单地说,各种基于DOM规范解析器必须按照DOM规范在内存中建立数据, DOM规范的核心是树模型。对于解析XML文件的解析器,解析器通过读入XML文件在内存中建立一个树,也就是说XML文件的标记、标记的文本内容、实体等都会和内存中树的某个节点相对应。一个应用程序可以方便地操作内存中树的节点来处理XML文档,获取自己所需要的数据。 2.DOM解析器 我们主要介绍Sun公司的DOM解析器,Sun公司的解析器是支持DOM level 3的解析器。按照W3C制定的DOM规范,Sun公司发布的SDK1.5中提供了解析XML文件的API(Java API for XML Parsing,JAXP),JAXP实现了DOM规范的Java语言绑定,给出了DOM规范指定的接口,并给出实现这些接口的类集合。 可以登录到Sun公司的网站:http://java.sun.com,免费下载SDK1.5(例如:j2sdk-1_5_1-windows-i586.exe)。 首先让我们简单地了解一下JAXP,有关细节将分别讲述。 在JAXP中,DOM解析器是一个DocumentBuilder类的实例,该实例由Document BuilderFactory负责创建,步骤如下。 使用javax.xml.parsers包中的DocumentBuilderFactory类调用其类方法newInstance()实例化一个DocumentBuilderFactory对象: DocumentBuilderFactory  factory= DocumentBuilderFactory. newInstance(); factory对象调用newDocumentBuilder()方法返回一个DocumentBuilder对象(称做DOM解析器),例如: DocumentBuilder  builder=factory. newDocumentBuilder(); DocumentBuilder 类在javax.xml.parsers包中。 builder对象调用public Document parse(File f)方法解析参数f指定的文件,并将解析内容以对象的形式返回,该对象是实现了Document接口的一个实例,例如: Document  document= builder. parse(new File("price.xml")) ; Document 接口在org.w3c.dom包中。 注意:如果想要DocumentBuilderFactory产生的DOM 解析器支持名称空间,可以让factory对象调用setNamespaceAware(boolean b)方法: factory.setNamespaceAware (true); 对象Document是由实现了Node接口的类的实例构成的,这些实例称做Document对象中的节点。Document对象中的节点形成树状结构,也就是说XML文件的标记、标记的文本内容、实体等都会和对象Document中的某个节点相对应。现在,应用程序只要分析内存中的树状结构数据Document,就可以获得XML文件中的各种数据了。比如Document调用 NodeList getElementsByTagName(String str) 方法,返回一个实现了NodeList接口的对象,该对象也是由一些Node对象组成的,例如: NodeList  nodelist=document. getElementsByTagName("车次"); NodeList 接口在org.w3c.dom包中。假如XML文件中有两个名称为“车次”标记,那么 nodelist.getLength(); 返回的值是2,即nodelist刚好含有2个Node对象。 DOM解析器经常使用下述3个方法解析XML文件: public Document parse(File f) throws SAXException, IOException public Document parse(InputStream in) throws SAXException, IOException public Document parse(String uri) throws SAXException, IOException 方法parse(File f)可以解析参数f指定的XML文件,例如: File f= new File("price.xml"); Document  document= builder. parse(f) ; 方法parse(InputStream in)可以解析输入流参数in指向的XML文件,例如: FileInputStream in=new FileInputStream("price.xml"); Document  document= builder. parse(in) ; 方法parse(String uri)可以解析参数uri指定的一个有效的资源,如果uri是一个链接地址,该链接地址必须是可以访问的,例如: String  uri="http://192.168.2.1/price.xml"; Document  document= builder. parse(uri) ; 6.2  节点的类型 1.Node接口 我们已经知道,解析器调用parse方法返回一个实现了Document接口的实例,该实例也称做Document对象,它是由实现了Node接口的实例组成的树状结构数据,这些实例称做Document对象中的节点。实际上Document接口也是Node接口的子接口,也就是说,parse方法将整个被解析的XML文件封装成一个节点返回(XML文件和内存中的Document节点相对应),因此,我们也可以称Document对象为Document节点。应用程序可以从Document节点的子孙节点中获取整个XML文件中数据的细节。Document节点可以有Element子节点(一个Element节点就是实现了Element接口的实例),而Element节点还可以有Elemen子节点等等。Document节点的子孙节点封装着被解析文件中的相应数据,比如,XML文件中根标记恰好和Document节点的惟一一个Element子节点相对应,而XML文件中根标记的孙标记恰好对应Document节点的Element子节点的某个Element子节点,XML文件中标记的内容恰好对应Document节点的某个Element子孙节点的一个Text子节点等等。 按照DOM规范,Node接口有如下的子接口: Attr, CDATASection, CharacterData, Comment, Document, DocumentFragment, DocumentType, Element, Entity, EntityReference, Notation, ProcessingInstruction, Text 任何实现上述某个接口的类的实例都称做一个节点。 2.Node 接口的常用方法 一个节点可以调用 short getNodeType() 方法返回一个表示节点类型的常量(Node接口规定的常量值),例如,对于Element节点,getNodeType()方法返回的值为: Node.ELEMENT_NODE 节点可以调用 NodeList  getChildNodes() 返回一个由当前节点的所有子节点组成的NodeList对象。节点调用 Node  getFirstChild() 返回当前节点的第一个子节点。节点调用 Node  getLastChild() 返回当前节点的最后一个子节点。节点可以调用 NodeList  getTextContent() 返回当前节点及所有子孙节点中的文本内容。 3.节点的子孙关系 为了解析规范的XML文件,DOM规范规定了各种类型节点之间可以形成的子孙关系,比如,Document节点有且仅有一个Element节点,也可以有一个DocumentType节点(规范的XML文件有且仅有一个根标记,也可以有一个与其关联的DTD文件),Element节点可以有Element子节点和Text子节点(规范的XML文件中的标记可以有子标记和文本)。图6.1是DOM规范所规定的节点之间可以形成的子孙关系: 通过图6.1可知,JAXP中DOM解析器的 parse方法返回的刚好是DOM规范规定的根节点Document,其他类型节点都是此根节点的子孙节点。从下节开始,我们将详细地介绍这些子孙节点。 图6.1  节点的子孙关系 6.3  Document节点 我们已经知道,解析器的parse方法将整个被解析的XML文件封装成一个Document节点返回,应用程序可以从该节点的子孙节点中获取整个XML文件中数据的细节。 Document节点的两个直接子节点的类型分别是DocumentType类型和Element类型,其中的DocumentType节点对应着XML文件所关联的DTD文件,通过进一步获取该节点子孙节点来分析DTDL文件中的数据;而其中的Element类型节点对应着XML文件的根节点,通过进一步获取该Element类型节点子孙节点来分析XML文件中的数据。 Document 节点经常使用下列方法获取和该节点相关的信息。 Element getDocumentElement() 返回当前节点的Element子节点。 DocumentType getDoctype() 返回当前节点的DocumentType子节点。 NodeList getElementsByTagName(String name) 返回一个NodeList对象,该对象由当前节点的Element类型子孙节点组成,这些子孙节点的名字由参数name指定。 NodeList getElementsByTagNameNS(String namespaceURI,String localName) 返回一个NodeList对象,该对象由当前节点的Element类型子孙节点组成,      这些子孙节点的名字由参数localName指定,名称空间由参数 namespaceURI    指定。 String getXmlEncoding() 返回XML文件使用的编码,即XML声明中encoding属性的值。 boolean getXmlStandalone()  返回XML声明中的standalone属性的值。 String getXmlVersion() 返回XML声明中的version属性的值 下面的例子1中,使用DOM解析器解析 “cha6_1.xml”中的数据,其中“JAXPOne. java”的运行效果如图6.2所示。 图6.2  使用DOM解析器 例子1(效果如图6.2所示) Cha6_1.xml <雇员列表> <雇员> sdfs <姓名> 张三 <年龄> 25岁 <工资> 3190元/月   <雇员> <姓名> 李四 <年龄> 35岁 <工资> 4320元/月 <雇员> <姓名> 王五 <年龄> 45岁 <工资> 5610元/月 JAXPOne.java import org.w3c.dom.*; import javax.xml.parsers.*; import java.io.*; public class JAXPOne { public static void main(String args[]) { try { DocumentBuilderFactory  factory= DocumentBuilderFactory. newInstance(); DocumentB uilder  builder= factory. newDocumentBuilder(); Document  document= builder. parse(new File("Cha6_1.xml")) ; String version= document.getXmlVersion(); System.out.println("XML声明的版本号:"+version); String encoding= document.getXmlEncoding(); System.out.println("XML声明的的编码:"+encoding); Element root=document.getDocumentElement() ; String  rootName=root.getNodeName(); System.out.println("XML文件根节点的名字:"+rootName);  NodeList  nodelist=document.getElementsByTagName("雇员"); int size=nodelist.getLength(); for(int k=0;k <图书摘要> Java是一门很优秀的语言,具有平台无关等特点 JS P是Sun公司推出的动态网页技术标准 XML是万维网联盟定义的一种语言 JAXPTwo.java import org.w3c.dom.*; import javax.xml.parsers.*; import java.io.*; public class JAXPTwo { public static void main(String args[]) { try { DocumentBuilderFactory  factory= DocumentBuilderFactory. newInstance(); DocumentBuilder  builder= factory. newDocumentBuilder(); Document  document= builder. parse(new File("Cha6_2.xml")) ; Element root=document.getDocumentElement() ; String  rootName=root.getNodeName(); System.out.println("XML文件根节点的名字:"+rootName); NodeList nodeList=root.getChildNodes(); int size=nodeList.getLength(); for(int k=0;k … 4个Text节点分别对应XML文件中根标记和4个子标记之间形成的4个空白区: “<图书摘要>”与“”之间的空白区, “”与“”之间的空白区, “”与“”之间的空白区, “”与“”之间的空白区。 这些空白区都是为了使得XML文件看起来更美观而形成的,但解析器并不知道这一点,所以解析器仍然认为它们是有用的文本数据(由空白类字符组成)。另外,3个Element节点各自都有一个Text节点,分别对应着“Java基础教程”、“JSP基础教程”和“XML基础教程”标记中的文本数据。 解析器获得的Document节点及子孙节点的示意图如图6.4所示 图6.4  Document节点的子孙节点 6.5  Text节点 我们已经知道,规范的XML文件的非空标记可以有子标记和文本内容。在DOM规范中,解析器使用Element节点封装标记,用Text节点封装标记的文本内容,即Element节点可以有Element子节点和Text节点。例如,对于下列标记: <姓名>张小三 <性别>男 <年龄>23 该标记对应的Element节点共有7个子孙节点,其中2个Element子节点、3个Text子节点和2个Text孙节点。这些节点和XML中的标记及文本有如下的对应关系。 2个Element子节点分别对应“姓名”标记的2个子标记:“性别”和“年龄”。 3个Text子节点分别对应着:“<姓名>”与“<性别>”之间的文本、“”与“<年龄>”之间的空白类字符、“”之间的空白类字符。 两个Text孙节点分别对应标记“性别”和“年龄”的文本内容。 表示Text节点的常量是Node.TEXT_NODE,一个节点调用 short getNodeType() 方法返回的值如果等于Node.TEXT_NODE,那么该节点就是Text节点。 Text节点使用String getWholeText()方法获取节点中的文本(包括其中的空白字符)。 注意:对于Text节点,getNodeName()方法返回的是“#text”。 下面的例子3给出了XML文件和解析该文件的Java源文件,在Java源文件中, Text节点使用getWholeText()方法获得文本数据,效果如图6.5所示。 图6.5  输出Text节点中的文本数据 例子3(效果如图6.5所示) Cha6_3.xml <商品信息> <名称> 电视机 <生产日期> 2005年 <库存量>  120台 <单价>    2390元 <名称> 洗衣机 <生产日期> 2005年 <库存量>  234台 <单价>   1234元 JAXPThree.java import org.w3c.dom.*; import javax.xml.parsers.*; import java.io.*; public class JAXPThree { public static void main(String args[]) { OutContent utContent=new OutContent(); try { DocumentBuilderFactory  factory= DocumentBuilderFactory. newInstance(); DocumentBuilder  builder= factory. newDocumentBuilder(); Document  document= builder.parse(new File("Cha6_3.xml")) ; Element root=document.getDocumentElement() ; String  rootName=root.getNodeName(); System.out.println("XML文件根节点的名字:"+rootName); NodeList nodeList=root.getChildNodes(); outContent.output(nodeList); System.out.println("一共有"+outContent.m+"个Text节点"); }           catch(Exception e) { System.out.println(e); } } } class  OutContent {  int m=0; public void output(NodeList nodeList) { int size=nodeList.getLength(); for(int k=0;k <工资> <姓名> 赵一 <月薪 职称="教授">4567 <姓名> 钱二 <月薪 职称="讲师">2200 <姓名> 孙三 <月薪 职称="副教授">3000 <姓名> 李四 <月薪 职称="教授">4765 <姓名> 周五 <月薪 职称="讲师">2200 JAXPFour.java import org.w3c.dom.*; import javax.xml.parsers.*; import java.io.*; public class JAXPFour { public static void main(String args[]) { OutTotalMoney utContent=new OutTotalMoney(); try { DocumentBuilderFactory  factory= DocumentBuilderFactory. newInstance(); DocumentBuilder  builder= factory. newDocumentBuilder(); Document  document= builder. parse(new File("Cha6_4.xml")) ; Element root=document.getDocumentElement() ; String  rootName=root.getNodeName(); System.out.println("XML文件根节点的名字:"+rootName); NodeList nodeList=root.getChildNodes(); outContent.output(nodeList); System.out.println("高级职称的工资月总额:"+outContent.sumProfessor); System.out.println("中级职称的工资月总额:"+outContent.sumLecture); System.out.println("工资月总额:"+outContent.sumAll); }           catch(Exception e) { System.out.println(e); } } } class  OutTotalMoney {  double sumAll,sumProfessor,sumLecture; public void output(NodeList nodeList) { int size=nodeList.getLength(); for(int k=0;k”作为段的结束,段开始和段结束之间称为CDATA段的内容,解析器不对CDATA段的内容做分析处理,因此,CDATA段中的内容可以包含任意的字符。 在DOM规范中,解析器使用CDATASection节点封装CDATA段,CDATASection节点可以是Element的节点的子节点。例如,下列标记   北京 ]]> 上海 ]]> 中含有普通字符数据和CDATA段。当一个标记的文本内容中含有CDATA段时,该标记对应的Element节点就会有Text子节点和CDATASection子节点,节点数目的计算办法如下。 首先将标记中交替出现的普通文本和CDATA段按照它们在标记中出现的先后顺序排列,如: 普通文本1  CDATA段1  普通文本2  CDATA段2  普通文本3 那么该标记对应的Element节点的子节点顺序如下。 1 /  Text节点:从“普通文本1”到“普通文本3”的区域,节点的文本内容是普通文本和CDATA段中的内容。 2 / CDATASection节点:从“CDATA段1”到“普通文本3”的区域,节点的文本内容是普通文本和CDATA段中的内容。 3 /  Text节点:从“普通文本2”到“普通文本3”的区域,节点的文本内容是普通文本和CDATA段中的内容。 4 / CDATASection节点:从“CDATA段2”到“普通文本3”的区域,节点的文本内容是普通文本和CDATA段中的内容。 5 / Text节点:“普通文本3”,节点的文本内容是普通文本。 上述的标记“hi”对应的Element节点有5个子节点,其中3个Text节点,2个CDATASection节点(注意,“hi”标记中最后的普通文本是回车符:¶)。 表示CDATASection节点的常量是Node.CDATA_SECTION_NODE,一个节点调用 short getNodeType() 方法返回的值如果等于Node.CDATA_SECTION_NODE,那么该节点就是CDATASection节点。 CDATASection节点使用String getWholeText()方法获取节点中的文本,即CDATA段中的文本(包括其中的空白字符)。 注意:对于CDATASection节点,getNodeName()方法返回的是“#cdata-section”。 在下面的例子5中,Java应用程序获取Element节点的Text子节点和CDATASection子节点,并输出它们含有的文本数据。 例子5(效果如图6.7所示) 图6.7  获取CDATA段的内容 Cha6_5.xml 早上 中午 晚上 JAXPFive.java import org.w3c.dom.*; import javax.xml.parsers.*; import java.io.*; public class JAXPFive { public static void main(String args[]) { try {  int n=0; DocumentBuilderFactory  factory= DocumentBuilderFactory. newInstance(); DocumentBuilder  builder= factory. newDocumentBuilder(); Document  document= builder. parse(new File("Cha6_5.xml")) ; NodeList nodeList=document.getElementsByTagName("hi"); int size=nodeList.getLength(); for(int k=0;k <桌子 width="300" height="600" length="500"> 吃饭用的 <桌子 width="700" height="500" length="1000"> 学习用的 JAXPSix.java import org.w3c.dom.*; import javax.xml.parsers.*; import java.io.*; public class JAXPSix { public static void main(String args[]) { try { DocumentBuilderFactory  factory= DocumentBuilderFactory. newInstance(); DocumentBuilder  builder= factory. newDocumentBuilder(); Document  document= builder. parse(new File("Cha6_6.xml")) ; Element root=document.getDocumentElement() ; String  rootName=root.getNodeName(); System.out.println("XML文件根节点的名字:"+rootName);  NodeList  nodelist=root.getElementsByTagName("桌子"); int size=nodelist.getLength(); for(int i=0;i
下载需要 8 金币 [金币充值 ]
亲,您也可以通过 分享原创文档 来获得金币奖励!

下载文档到电脑,查找使用更方便

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 8 金币 [ 分享文档获得金币 ] 0 人已下载

下载文档