370 likes | 549 Views
第五章. DOM. 课程目标. DOM 简介。 DOM 的接口和类。 DOM 的用法。 JDOM 的用法。. 体验项目 ——< 使用 DOM 解析并更新 XML 文档 >. 使用 DOM 解析关于学生信息的 XML 文档,然后使用 DOM 的接口向该文档中添加关于教师的信息。最后再用改动后的 XML 文档替换该文档。程序运行前,学生信息的 XML 文档( class.xml )内容,如下图所示:. 运行前文档的内容图. 程序运行后,学生信息的 XML 文档内容如下图所示:. 运行后文档的内容图. DOM 简介. 了解 DOM.
E N D
第五章 DOM
课程目标 • DOM简介。 • DOM的接口和类。 • DOM的用法。 • JDOM的用法。
体验项目——<使用DOM解析并更新XML文档 > 使用DOM解析关于学生信息的XML文档,然后使用DOM的接口向该文档中添加关于教师的信息。最后再用改动后的XML文档替换该文档。程序运行前,学生信息的XML文档(class.xml)内容,如下图所示: 运行前文档的内容图
程序运行后,学生信息的XML文档内容如下图所示: 运行后文档的内容图
DOM简介 了解DOM DOM是Document Object Model的缩写,即文档对象模型。W3C已于2000年11月13日推出了DOM level 2规范。DOM是HTML和XML文档的编程接口规范,它与平台和语言是无关的,因而可以用各种语言和在各种平台上实现。 该模型定义了HTML和XML文件在内存中文档结构,提供了对HTML和XML文件的访问、存取方法。利用DOM规范,可以实现DOM文档和XML之间的相互转换,对相应DOM文档的内容进行遍历或其他操作。如果要自由的操纵XML文件,就要用到DOM规范。 DOM的原理简单的说,就是通过解析XML文档,为XML文档在逻辑上建立一个树模型,树的节点是一个个对象。我们通过存取这些对象就能够操作XML文档中的内容了。
DOM的优缺点 DOM的优势主要表现在:易用性强,使用DOM时,将把所有的XML文档信息都存于内存中,并且遍历简单,支持XPath,增强了易用性。 DOM的缺点主要表现在:效率低,解析速度慢,内存占用量过高,对于大文件来说几乎不可能使用。另外效率低还表现在大量的消耗时间,因为使用DOM进行解析时,将为文档的每个element、attribute、processing-instrUCtion和comment都创建一个对象,这样在DOM机制中所运用的大量对象的创建和销毁无疑会影响其效率。
文档模型 Java中的可用文档模型数一直在增加。除了DOM外,还有以下几种: JDOM JDOM的目的是成为Java特定文档模型,JDOM仅使用具体类而不使用接口,并大量使用了集合类 是JDOM的一种智能分支,包括集成的XPath支持、Schema支持以及用于大文档或流化文档的基于事件的处理 dom4j 是支持分布式计算的商业项目的附属产物,只能适当地支持XML文档的子集,它没有为验证提供任何支持并且有更严格的许可证 Electric XML(EXML) XPP只能适当支持XML文档的子集,并且不支持对文档提供验证 XML Pull Parser (XPP)
DOM类的介绍 DocumentBuilderFactory类 DocumentBuilderFactory类是一个抽象类,该类主要用于定义工厂API,使应用程序能够从XML文档获取生成DOM对象树的解析器。该类位于javax.xml.parsers包中,只有一个受保护的构造方法。其构造方法如下: protected DocumentBuilderFactory( )方法:用于阻止实例化的受保护构造方法。 该类还包括以下几种常用的方法: static DocumentBuilderFactory newInstance( )获取DocumentBuilderFactory的新实例 static DocumentBuilderFactory newInstance(String factoryClassName, ClassLoader classLoader) abstract DocumentBuilder newDocumentBuilder( ) void setValidating(boolean validating)指定是否由此代码生成的解析器来验证被解析的文档。 void setIgnoringComments(boolean ignoreComments)指定由此代码生成的解析器是否忽略注释 void setExpandEntityReferences(boolean expandEntityRef):指定由此代码生成的解析器是否扩展实体引用节点。
DocumentBuilder类 DocumentBuilder类是一个抽象类,主要用于从XML文档获取DOM文档实例。可以通过DocumentBuilderFactory.newDocumentBuilder();代码获取此类的实例。获取此类的实例之后,将可以从各种输入流来解析XML。 该类有一个受保护的构造方法 如下: protected DocumentBuilder( )。 DocumentBuilder类其他的常用方法如下: (1)Document parse(String uri)方法:将给定URI的内容解析为一个XML文档,并且返回一个新的DOM Document对象。 (2)abstract Document newDocument( )方法:获取DOM Document对象的一个新实例来生成一个DOM树。 (3)void reset( )方法:将此DocumentBuilder重置为其原始配置。 (4)abstract void setEntityResolver(EntityResolver er)方法:指定使用EntityResolver解析要解析的XML文档中存在的实体。 (5)abstract void setErrorHandler(ErrorHandler eh)方法:指定解析器要使用的ErrorHandler。
DOM的四个基本接口介绍 Document接口 Document接口表示整个HTML或XML文档。它不仅指文档的根,并提供对文档数据的基本访问。Document接口是对文档进行操作的入口,它是从Node接口继承过来的。该接口位于org.w3c.dom包中。 该接口的常用方法有以下几种: Node adoptNode(Node source)方法:试图把另一文档中的节点采用到该文档中。 Attr createAttribute(String name)方法:根据给定的名称name创建Attr。 Element createElement(String tagName)方法:创建指定类型的元素。 Element getElementById(String element)方法:返回名为element的Element对象。 NodeList getElementsByTagName(String tagname)方法:按文档顺序返回包含在文档中且具有给定标记名称的所有元素的列表。 Node importNode(Node importedNode, boolean deep)方法:将另一文档的importedNode节点插入到该文档中,而不改变或移除原始文档中的原节点。 Text createTextNode(String data)方法:根据给定字符串data创建Text节点。
NodeList接口 NodeList接口提供对节点的有序集合的抽象,没有定义或约束如何实现此集合。该接口位于org.w3c.dom包中,NodeList对象是不断变化的。 主要方法有以下两个: int getLength()方法:列表中的节点数。 Node item(int index)方法:返回集合中的索引为index的节点。如果index大于或等于此列表中的节点数,则返回null。
Node接口 Node接口是其他大多数接口的父类,像Document、Element、Attribute、Text、Comment等接口都是从Node接口继承过来的。它表示该文档树中的单个节点。 常用的方法有以下几种: Node appendChild(Node newChild)方法:将节点newChild添加到此节点的子节点列表的末尾。 NamedNodeMap getAttributes()方法:返回包含此节点的属性的NamedNodeMap对象;否则为null。 Node getFirstChild()方法:此节点的第一个子节点。 Node getLastChild()方法:此节点的最后一个节点。 Node getNextSibling()方法:直接在此节点之后的节点。 String getNodeValue()方法:此节点的值,取决于其类型; Node getParentNode()方法:此节点的父节点。 Node removeChild(Node oldChild)方法:从子节点列表中移除oldChild所指示的子节点,并将其返回。 注意:在DOM中每个元素的字符数据(元素值)也是一个Node对象。
NodeList和Node示例 import javax.xml.parsers.*; import org.w3c.dom.*; public class TestNode{ public static void main(String args[ ]){ try{ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder=factory.newDocumentBuilder(); Document doc=builder.parse("test.xml"); NodeList list=doc.getElementsByTagName("student"); for (int i=0;i<list.getLength();i++){ Element node=(Element)list.item(i); System.out.print("name: "); System.out.println (node.getElementsByTagName ("name").item(0).getFirstChild().getNodeValue()); System.out.print("age: "); System.out.println (node.getElementsByTagName("age"). item(0).getFirstChild().getNodeValue()); System.out.print("address: "); System.out.println (node.getElementsByTagName ("address").item(0).getFirstChild().getNodeValue()); System.out.println();} }catch(Exception e){e.printStackTrace();} } } 打印元素name的信息 打印age元素的信息 打印address元素的信息
test.xml文件的内容如下: <class> <student> <name>zhangsan</name> <age>20</age> <address>china</address> </student> <student> <name>lisi</name> <age>25</age> <address>china</address> </student> </class> 运行效果图
NamedNodeMap接口 实现NamedNodeMap接口的对象用于表示可以通过名称访问的节点的集合。 NamedNodeMap表示的是一组节点和其唯一名称的一一对应关系,这个接口主要用在属性节点的表示上。 在实现NamedNodeMap的对象中所包含的Node对象还可以通过顺序索引进行访问,但并不意味着DOM指定这些节点的顺序。 在DOM中NamedNodeMap对象也是不断变化的。
NamedNodeMap接口主要方法 NamedNodeMap接口主要有以下几种方法: int getLength()方法:返回该NamedNodeMap对象中的节点数。 Node getNamedItem(String name)方法:返回名称为name的节点。 Node getNamedItemNS(String namespaceURI, String localName)方法:返回通过本地名称和名称空间URI所指定的节点。 Node item(int index)方法:返回索引值为index的项。 Node removeNamedItem(String name)方法:移除名称为name的节点。 Node removeNamedItemNS(String namespaceURI, String localName)方法:移除通过本地名称和名称空间URI所指定的节点。 Node setNamedItem(Node arg)方法:使用节点名称添加节点。 Node setNamedItemNS(Node arg)方法:使用其namespaceURI和localName添加节点。
import javax.xml.parsers.*; import org.w3c.dom.*; public class TestNamedNodeMap{ public static void main(String args[ ]){ try{ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder=factory.newDocumentBuilder(); Document doc=builder.parse("student.xml"); NodeList list=doc.getElementsByTagName("student"); for (int i=0;i<list.getLength();i++){ Node node=list.item(i); NamedNodeMap np=node.getAttributes(); System.out.println("第"+(i+1) +"个student元素的属性信息:"); for(int j=0;j<np.getLength();j++){ Node attr=np.item(j); System.out.println(attr.getNodeName()+"=" +attr.getNodeValue()+""); } System.out.println(); } }catch(Exception e){e.printStackTrace();} } } 获得属性集合
student.xml的文件内容如下: <class> <student id="A002" name="zhangsan" age="24"></student> <student id="D002" name="lisi" age="23"></student> </class> 运行效果图
DOM的使用 使用DOM解析XML文档并打印 XML文档内容如下(candidate.xml): <?xml version="1.0" encoding="UTF-8"?> <PEOPLE> <PERSON PERSONID="E01" age="30"> <NAME>Tony Blaa</NAME> <ADDRESS>10 Downing Street, London, UK</ADDRESS> <TEL>(061) 98765</TEL> <EMAIL>blair@everywhere.com</EMAIL> </PERSON> <PERSON PERSONID="E02" sex="F"> <NAME>Bill Clinton</NAME> <ADDRESS>White House, USA</ADDRESS> <TEL>(001) 6400 98765</TEL> <EMAIL>bill@everywhere.com</EMAIL> </PERSON> <PERSON PERSONID="E03" age="25"> <NAME>Tom Cruise</NAME> <ADDRESS>57 Jumbo Street, New York, USA</ADDRESS> <TEL>(001) 4500 67859</TEL> <EMAIL>cruise@everywhere.com</EMAIL> </PERSON> <PERSON PERSONID="E04" sex="F"> <NAME>Linda Goodman</NAME> <ADDRESS>78 Crax Lane, London, UK</ADDRESS> <TEL>(061) 54 56789</TEL> <EMAIL>linda@everywhere.com</EMAIL> </PERSON> </PEOPLE>
import javax.xml.parsers.*; import org.w3c.dom.*; public class DomJiexi1{ public static void main(String args[ ]){ try{ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder=factory.newDocumentBuilder(); Document doc=builder.parse("candidate.xml"); NodeList list=doc.getElementsByTagName("PERSON"); for (int i=0;i<list.getLength();i++){ Element node=(Element)list.item(i); NamedNodeMap np=node.getAttributes(); System.out.print("第"+(i+1) +"个PERSON元素的属性信息:"); for(int j=0;j<np.getLength();j++){ Node attr=np.item(j); System.out.print("\t"+attr.getNodeName() + "="+ attr.getNodeValue()+""); } System.out.print("\n元素信息:");
System.out.print("NAME: "); System.out.println (node.getElementsByTagName ("NAME").item(0).getFirstChild().getNodeValue()); System.out.print("ADDRESS: "); System.out.println (node.getElementsByTagName ("ADDRESS").item(0). getFirstChild().getNodeValue()); System.out.print("TEL: "); System.out.println (node.getElementsByTagName ("TEL").item(0).getFirstChild().getNodeValue()); System.out.print("EMAIL: "); System.out.println (node.getElementsByTagName ("EMAIL").item(0).getFirstChild().getNodeValue()); System.out.println(); } }catch(Exception e){ e.printStackTrace(); } } } 运行效果图
使用递归解析DOM树 使用递归解析DOM树的主要思想是对节点进行递归,判断是否有子节点。如果有就打印出元素名,然后继续判断。反之就是字符数据,将其打印出来。如下: public void aaa(NodeList n){ for(int i=0;i<n.getLength();i++){ Node node=n.item(i); if(node.hasChildNodes()){ NodeList m=node.getChildNodes(); System.out.println(node.getNodeName()); this.aaa(m); }else{ if(!node.getNodeValue().trim().equals("")){ System.out.println("值:" + node.getNodeValue().trim()); } } } } 调用函数自身
建立和更新XML文档 人工建立DOM树 import javax.xml.parsers.*; import org.w3c.dom.*; import org.apache.crimson.tree.XmlDocument; public class JianXML1{ public static void main(String args[ ])throws Exception{ DocumentBuilderFactory factory= DocumentBuilderFactory.newInstance(); DocumentBuilder db=factory.newDocumentBuilder(); Document doc=db.newDocument(); //添加根元素 Element root=doc.createElement("person"); doc.appendChild(root); //添加元素“student”为“person”的子元素 Element e_student=doc.createElement("student"); root.appendChild(e_student); //为元素“student”添加属性 Attr a=doc.createAttribute("bianhao"); a.setNodeValue("A001");
e_student.setAttributeNode(a); Element e_name=doc.createElement("name"); e_student.appendChild(e_name); //为元素“name”添加字符数据信息 Text t=doc.createTextNode("zhangsan"); e_name.appendChild(t); //调用XmlDocument类的write()方法 XmlDocument xmldoc=(XmlDocument)doc; xmldoc.write(System.out); } } 运行效果图
使用XmlDocument类更新文档 XmlDocument是org.apache.crimson.tree包中的一个类,并不包含于标准的JAXP中。在我们使用该类时必须要加载相应的类库到JDK中。 使用XmlDocument类主要是应用该类的write()方法,有如下三种形式: public void write (OutputStream out) public void write (Writer out) public void write (Writer out, String encoding) doc是一个Document对象实例(强制类型转换) 使用write()方法将DOM树保存为文件的形式 ,如下: XmlDocument xmldom=(XmlDocument)doc; xmldom.write(new FileOutputStream(new File("Xmldom.xml")));
使用TransformerFactory类和Transformer类更新文档 TransformerFactory类 TransformerFactory类的实例可用于创建Transformer和Templates对象。该类位于javax.xml.transform包中。该类是一个抽象类。它的默认构造方法受到有意保护。主要有以下几种常用方法: static TransformerFactory newInstance()方法:获取TransformerFactory的新实例。 static TransformerFactory newInstance(String factoryClassName, ClassLoader classLoader)方法:根据类名称获得一个新TransformerFactory实例。 abstract Templates newTemplates(Source source)方法:将Source处理为Templates对象,后者是源编译后的表示形式。 abstract Transformer newTransformer( )方法:返回执行从Source到Result的复制的新 Transformer。
Transformer类 Transformer类的实例可以处理来自不同源的XML,并将转换输出写入各种接收器。该类位于javax.xml.transform包中。该类是一个抽象类。它的默认构造方法受到有意保护。但可以通过TransformerFactory.newTransformer方法获取该类的实例。主要的常用方法有以下几种: abstract void transform(Source xmlSource, Result outputTarget)方法:将XML Source转换为Result。 void reset()方法:将此Transformer重置为其初始配置。 abstract void setErrorListener(ErrorListener listener)方法:设置转换的实际错误事件侦听器。 abstract void setOutputProperties(Properties oformat)方法:设置转换的输出属性。
使用TransformerFactory和Transformer类更新文档的主要思想是:使用TransformerFactory和Transformer类更新文档的主要思想是: 调用TransformerFactory类的静态方法newInstance( )获得一个TransformerFactory对象,由该对象的newTransformer()方法创建了一个Transformer对象。调用Transformer对象的transform()方法即可。注意 transform()方法需要两个参数第一个参数是DOMSource对象(与DOM树有关),第二个参数是StreamResult对象(与输出有关)。 使用方法如下: doc是一个Document对象实例 DOMSource doms = new DOMSource (doc); StreamResult sr = new StreamResult(new FileOutputStream(new File("a.xml"))); TransformerFactory tf=TransformerFactory.newInstance(); Transformer t=tf.newTransformer (); t.transform(doms,sr);
从因特网上获取天气数据示例 按URL字符串打开一个URLConnection。由于我们知道天气站点是通过HTTP请求进行操作的,所以可以将纯URLConnection强制转型为HttpURLConnection。接着,测试来自服务器的响应代码。如果通过,那么就打开连接流。解析器开始解析数据流并把数据流转换成Document。 public static Document getDocument(DocumentBuilder db, String urlString){ try{ URL url=new URL(urlString); URLConnection URLconnection=url.openConnection(); HttpURLConnection httpConnection=(HttpURLConnection)URLconnection; int responseCode=httpConnection.getResponseCode(); if(responseCode==HttpURLConnection.HTTP_OK){ InputStream in=httpConnection.getInputStream(); Document doc=db.parse(in); return doc; }else{ System.out.println("HTTP connection response HTTP_OK"); } }catch(Exception e){ e.printStackTrace() ; } return null; } 将纯URLConnection强制转型为HttpURLConnection
public static void main(String args[ ]){ try{ String cityname="beijing"; String url="http://www.weather.unisys.com/forexml.cgi?"+ cityname; DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance(); DocumentBuilder builder=factory.newDocumentBuilder(); Document doc=new SelectWeather().getDocument(builder,url); NamedNodeMap np1= doc.getElementsByTagName("forexml").item(0).getAttributes(); for(int j=0;j<np1.getLength();j++){ Node attr1=np1.item(j); System.out.println(attr1.getNodeName()+"="+ attr1.getNodeValue()); } NamedNodeMap np2=doc.getElementsByTagName ( "observation").item(0).getAttributes(); for(int j=0;j<np2.getLength();j++){ Node attr2=np2.item(j); System.out.println(attr2.getNodeName()+"="+ attr2.getNodeValue()); } NamedNodeMap np3=doc.getElementsByTagName ("almanac").item(0).getAttributes(); for(int j=0;j<np3.getLength();j++){ Node attr3=np3.item(j); System.out.println(attr3.getNodeName()+"="+ attr3.getNodeValue()); } }catch(Exception e){e.printStackTrace();} } 城市名称 天气站点
JDOM简介 JDOM是一个开源项目,它基于树型结构,利用纯Java的技术对XML文档实现解析、生成、序列化以及多种操作。JDOM 直接为Java编程服务。它利用强而有力的Java语言的诸多特性(方法重载、集合概念以及映射),把SAX和DOM的功能有效地结合起来。在使用设计上尽可能地隐藏原来使用XML过程中的复杂性。利用JDOM处理XML文档将是一件轻松、简单的事。 JDOM 在2000年的春天被Brett McLaughlin和Jason Hunter开发出来,以弥补DOM及SAX在实际应用中的不足。这些不足之处主要在于SAX没有文档修改、随机访问以及输出的功能,而对于DOM来说,Java程序员在使用时总觉得不太方便。 JDOM的最新版本为JDOM Beta 10。最近JDOM被收录到JSR-102内,这标志着JDOM成为了Java平台组成中的一部分。
JDOM类库 在使用JDOM时要下载JDOM类库,将其加载到JDK中。JDOM类库是由以下几个包组成:
JDOM解析带DTD验证的XML文档 import java.io.*; import org.jdom.*; import org.jdom.input.*; import org.jdom.output.*; public class TestJDOM1{ public void select(){ try { SAXBuilder builder=new SAXBuilder(true); Document doc=builder.build( new FileInputStream(new File("person.xml"))); XMLOutputter outputter=new XMLOutputter(); outputter.setEncoding("gb2312"); outputter.output(doc, System.out); }catch(Exception e){ System.out.println(e); } } public static void main(String args[ ]) { new TestJDOM1().select(); } } person.xml 文件是带DTD验证的XML文档
JDOM解析带有Schema验证的XML import java.io.*; import org.jdom.*; import org.jdom.input.*; import org.jdom.output.*; public class TestJDOMSchema{ String xml="family.xml"; String schema="family.xsd"; public void validate() { try{ SAXBuilder saxBuilder = new SAXBuilder(true); saxBuilder.setValidation(true); saxBuilder.setFeature("http://apache.org/xml/“ + "features/validation/schema", true); saxBuilder.setProperty( "http://apache.org/xml/properties/schema/" + "external-noNamespaceSchemaLocation",schema); Document jdomDoc = saxBuilder.build(new FileInputStream(new File(xml))); XMLOutputter xmlOutputter = new XMLOutputter(); xmlOutputter.setEncoding("gb2312"); xmlOutputter.output(jdomDoc, System.out); }catch(Exception e){ System.out.println("验证失败:"+e); } } public static void main(String args[]){ new TestJDOMSchema ().validate(); } } 指定约束方式为XML schema 导入Schema文件
使用JDOM编写XML文档 import java.io.*; import org.jdom.*; import org.jdom.input.*; import org.jdom.output.*; public class TestJDOM2{ public void Create() { try { Document doc = new Document(); Element root = new Element("根元素"); doc.setRootElement(root); Element el1 = new Element("first"); el1.setAttribute("属性", "属性值"); Text text1=new Text("字符数据"); Element em = new Element("second").addContent("元素二"); el1.addContent(text1); el1.addContent(em); Element el2 = new Element("third").addContent("元素三"); root.addContent(el1); root.addContent(el2); XMLOutputter outputter = new XMLOutputter("\t",true); outputter.setEncoding("gb2312"); outputter.output(doc, System.out); }catch(Exception e) { System.out.println(e); } } public static void main(String args[ ]) { new TestJDOM2 ().Create(); } } 中文处理 缩进,自动换行
实践项目——<使用DOM解析并更新XML文档 > 程序的实现要求如下: (1)创建解析器获得DOM树。 (2)更新DOM树,添加教师信息。 (3)处理中文问题。
本章总结 • DOM简介。 • DOM的接口和类。 • DOM的用法。 • JDOM的用法。