|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
关于第二点:俺问问你,如果是企业级项目的话,诸如RMI,EJB,等一些关键技术,这些难道都不需要学么?如果光是使用jsp,servlet,javabean的话。xml在本系列的第一篇文章中,我研讨了一些用Java编写的次要的XML文档模子的功能。可是,在入手下手选择这类范例的手艺时,功能只是成绩的一部分。利用便利最少是一样主要的,而且它已经是一个次要来由,来撑持利用Java特定的模子,而不是与言语有关的DOM。
为实在懂得哪一个模子真实的感化,您必要晓得它们在可用性水平上是怎样排名的。本文中,我将实验举行这个事情,从样本代码入手下手,来演示怎样在每一个模子中编码大众范例的操纵。并对了局举行总结来停止本文,并且提出了促使一种暗示比另外一种更简单利用的一些别的要素。
请参阅之前的文章(请参阅参考材料或本文“内容”下的便利链接)来猎取这个对照中利用的各个模子的背景材料,包括实践的版本号。还能够参阅“参考材料”一节中关于源代码下载、到模子主页的链接和别的相干信息。
代码对照
在对分歧文档暗示顶用法手艺的这些对照中,我将显现怎样在每种模子中完成三种基础操纵:
依据输出流构建文档
遍历元素和内容,并做一些变动:
从文本内容中撤除前导和跟随的空缺。
假如了局文本内容为空,就删除它。
不然,将它包装到父元素的称号空间中一个名为“text”的新元素中。
将已修正的文档写进输入流
这些示例的代码是以我在上篇文章中利用的基准程序为基本的,并举行了一些简化。基准程序的核心是为了显现每一个模子的最好功能;关于本文,我将实验显现在每种模子中完成操纵的最烦琐办法。
我已将每一个模子的示例布局化为两个自力的代码段。第一段是读取文档、挪用修正代码和编写已修正文档的代码。第二段是真正遍历文档暗示和实行修正的递回办法。为制止分离注重力,我已在代码中疏忽了非常处置。
您能够从本页底部参考材料一节链接到下载页,以猎取一切样本的完全代码。样本的下载版本包含一个测试驱动程序,另有一些增加的代码用于经由过程盘算元素、删除和增加的个数来反省分歧模子的操纵。
即便您不想利用DOM完成,但仍是值得扫瞄上面对DOM用法的形貌。由于DOM示例是第一个示例,以是与前面的模子比拟,我用它来探求有关该示例的一些成绩和布局的更具体信息。扫瞄这些内容能够增补您想晓得的一些细节,假如间接浏览别的模子之一,那末将错过这些细节。
DOM
DOM标准涵盖了文档暗示的一切范例的操纵,可是它没有触及比方对文档的语法剖析和天生文本输入如许的成绩。包含在功能测试中的两种DOM完成,Xerces和Crimson,对这些操纵利用分歧的手艺。清单1显现了Xerces的顶级代码的一种情势。
清单1.XercesDOM顶级代码
1//parsethedocumentfrominputstream("in")
2DOMParserparser=newDOMParser();
3parser.setFeature("http://xml.org/sax/features/namespaces",true);
4parser.parse(newInputSource(in));
5Documentdoc=parser.getDocument();
6//recursivelywalkandmodifydocument
7modifyElement(doc.getDocumentElement());
8//writethedocumenttooutputstream("out")
9OutputFormatformat=newOutputFormat(doc);
10XMLSerializerserializer=newXMLSerializer(out,format);
11serializer.serialize(doc.getDocumentElement());
正如我在正文中指出的,清单1中的第一块代码(第1-5行)处置对输出流的语法剖析,以构建文档暗示。Xerces界说了DOMParser类,以便从Xerces语法剖析器的输入构建文档。InputSource类是SAX标准的一部分,它能顺应供SAX剖析器利用的几种输出情势的任何之一。经由过程单一挪用举行实践的语法剖析和文档机关,假如乐成完成了这一操纵,那末使用程序就能够检索并利用已机关的Document。
第二个代码块(第6-7行)只是将文档的根元素传送给我即刻要谈到的递回修正办法。这些代码与本文中一切文档模子的代码在实质上是不异的,以是在残剩的示例中我将跳过它,不再做任何会商。
第三个代码块(第8-11行)处置将文档作为文本写进输入流。这里,OutputFormat类包装文档,并为格局化天生的文本供应了多种选项。XMLSerializer类处置输入文本的实践天生。
Xerces的modify办法只利用尺度DOM接口,以是它还与任何别的DOM完成兼容。清单2显现了代码。
清单2.DOMModify办法
1protectedvoidmodifyElement(Elementelement){
2//loopthroughchildnodes
3Nodechild;
4Nodenext=(Node)element.getFirstChild();
5while((child=next)!=null){
6//setnextbeforewechangeanything
7next=child.getNextSibling();
8//handlechildbynodetype
9if(child.getNodeType()==Node.TEXT_NODE){
10//trimwhitespacefromcontenttext
11Stringtrimmed=child.getNodeValue().trim();
12if(trimmed.length()==0){
13//deletechildifnothingbutwhitespace
14element.removeChild(child);
15}else{
16//createa"text"elementmatchingparentnamespace
17Documentdoc=element.getOwnerDocument();
18Stringprefix=element.getPrefix();
19Stringname=(prefix==null)?"text":(prefix+":text");
20Elementtext=
21doc.createElementNS(element.getNamespaceURI(),name);
22//wrapthetrimmedcontentwithnewelement
23text.appendChild(doc.createTextNode(trimmed));
24element.replaceChild(text,child);
25}
26}elseif(child.getNodeType()==Node.ELEMENT_NODE){
27//handlechildelementswithrecursivecall
28modifyElement((Element)child);
29}
30}
31}
清单2中显现的办法所利用的基础办法与一切文档暗示的办法不异。经由过程一个元素挪用它,它就顺次遍历谁人元素的子元素。假如找到文本内容子元素,要末删除文本(假如它只是由空格构成的),要末经由过程与包括元素不异的称号空间中名为“text”的新元从来包装文本(假如有非空格的字符)。假如找到一个子元素,那末这个办法就利用这个子元素,递回地挪用它自己。
关于DOM完成,我利用一对援用:child和next来跟踪子元素排序列表中我所处的地位。在对以后子节点举行任何别的处置之前,先装进下个子节点的援用(第7行)。如许做使得我可以删除或替换以后的子节点,而不丧失我在列表中的踪影。
当我创立一个新元从来包装非空缺的文本内容(第16-24行)时,DOM接口入手下手有点混乱。用来创立元素的办法与文档联系关系并成为一个全体,以是我必要在一切者文档中检索以后我正在处置的元素(第17行)。我想将这个新元素安排在与现有的父元素不异的称号空间中,而且在DOM中,这意味着我必要机关元素的限制称号。依据是不是着名称空间的前缀,这个操纵会有所分歧(第18-19行)。使用新元素的限制称号,和现有元素中的称号空间URI,我就可以创立新元素(第20-21行)。
一旦创立了新元素,我只需创立和增加文本节点来包装内容String,然后用新创立的元从来替换原始文本节点(第22-24行)。
清单3.CrimsonDOM顶级代码
1//parsethedocumentfrominputstream
2System.setProperty("javax.xml.parsers.DocumentBuilderFactory",
3"org.apache.crimson.jaxp.DocumentBuilderFactoryImpl");
4DocumentBuilderFactorydbf=DocumentBuilderFactoryImpl.newInstance();
5dbf.setNamespaceAware(true);
6DocumentBuilderbuilder=dbf.newDocumentBuilder();
7Documentdoc=builder.parse(in);
8//recursivelywalkandmodifydocument
9modifyElement(doc.getDocumentElement());
10//writethedocumenttooutputstream
11((XmlDocument)doc).write(out);
清单3中的CrimsonDOM示例代码利用了用于语法剖析的JAXP接口。JAXP为语法剖析和转换XML文档供应了一个尺度化的接口。本示例中的语法剖析代码还能够用于Xerces(对文档构建器类称号的特征设置有得当的变动)来替换较早给定的Xerces特定的示例代码。
在本示例中,我起首在第2行到第3行中设置体系特征来选择要机关的DOM暗示的构建器工场类(JAXP仅间接撑持构建DOM暗示,不撑持构建本文中会商的任何别的暗示)。仅当想选择一个要由JAXP利用的特定DOM时,才必要这一步;不然,它利用缺省完成。出于完全性起见,我在代码中包括了设置这个特征,可是更广泛的是将它设置成一个JVM命令行参数。
接着我在第4行到第6行中创立构建器工场的实例,对利用谁人工场实例机关的构建器启用称号空间撑持,并从构建器工场创立文档构建器。最初(第7行),我利用文档构建器来对输出流举行语法剖析并机关文档暗示。
为了写出文档,我利用Crimson中外部界说的基础办法。不包管在Crimson将来版本中撑持这个办法,可是利用JAXP转换代码来将文档作为文本输入的替换办法必要诸如Xalan那样的XSL处置器的。那超越了本文的局限,可是要猎取具体信息,能够查阅Sun中的JAXP教程。
JDOM
利用JDOM的顶级代码比利用DOM完成的代码略微复杂一点。为构建文档暗示(第1-3行),我利用带有由参数值克制考证的SAXBuilder。经由过程利用供应的XMLOutputter类,将已修正的文档写进输入流一样复杂(第6-8行)。
清单4.JDOM顶级代码
1//parsethedocumentfrominputstream
2SAXBuilderbuilder=newSAXBuilder(false);
3Documentdoc=builder.build(in);
4//recursivelywalkandmodifydocument
5modifyElement(doc.getRootElement());
6//writethedocumenttooutputstream
7XMLOutputterouter=newXMLOutputter();
8outer.output(doc,out);
清单5中JDOM的modify办法也比DOM的统一办法复杂。我猎取包括元素一切内容的列表并扫描了这张列表,反省文本(象String对象那样的内容)和元素。这张列表是“活的”,以是我能间接对它举行变动,而不用挪用父元素上的办法。
清单5.JDOMmodify办法
1protectedvoidmodifyElement(Elementelement){
2//loopthroughchildnodes
3Listchildren=element.getContent();
4for(inti=0;i<children.size();i++){
5//handlechildbynodetype
6Objectchild=children.get(i);
7if(childinstanceofString){
8//trimwhitespacefromcontenttext
9Stringtrimmed=child.toString().trim();
10if(trimmed.length()==0){
11//deletechildifonlywhitespace(adjustingindex)
12children.remove(i--);
13}else{
14//wrapthetrimmedcontentwithnewelement
15Elementtext=newElement("text",element.getNamespace());
16text.setText(trimmed);
17children.set(i,text);
18}
19}elseif(childinstanceofElement){
20//handlechildelementswithrecursivecall
21modifyElement((Element)child);
22}
23}
24}
创立新元素的手艺(第14-17行)十分复杂,并且与DOM版本分歧,它不必要会见父文档。
dom4j
dom4j的顶级代码比JDOM的略微庞大些,可是它们的代码行十分相似。这里的次要区分是我保留了用来构建dom4j文档暗示的DocumentFactory(第5行),并在输入已修正的文档文本以后革新了writer(第10行)。
清单6.dom4j的顶级代码
1//parsethedocumentfrominputstream
2SAXReaderreader=newSAXReader(false);
3Documentdoc=reader.read(in);
4//recursivelywalkandmodifydocument
5m_factory=reader.getDocumentFactory();
6modifyElement(doc.getRootElement());
7//writethedocumenttooutputstream
8XMLWriterwriter=newXMLWriter(out);
9writer.write(doc);
10writer.flush();
正如您在清单6中看到的,dom4j利用一个工场办法来机关文档暗示(从语法剖析构建)中包括的对象。依据接口来界说每一个组件对象,以是完成个中一个接口的任何范例的对象都能包括在暗示中(与JDOM相反,它利用详细类:这些类在某些情形中能够分别子类和被承继,可是在文档暗示中利用的任何类都必要以原始JDOM类为基本)。经由过程利用分歧工场举行dom4j文档构建,您能猎取分歧系列的组件中机关的文档。
在样本代码(第5行)中,我检索了用于构建文档的(缺省)文档工场,并将它存储在一个实例变量(m_factory)中以供modify办法利用。其实不严厉必要这一步―能够在一个文档中同时利用来自分歧工场的组件,大概能够绕过工场而间接创立组件的实例―但在该例中,我只想创立与在文档其他部分中利用的统一范例的组件,而且利用不异的工场来确保完成这个步骤。
清单7.dom4jmodify办法
1protectedvoidmodifyElement(Elementelement){
2//loopthroughchildnodes
3Listchildren=element.content();
4for(inti=0;i<children.size();i++){
5//handlechildbynodetype
6Nodechild=(Node)children.get(i);
7if(child.getNodeType()==Node.TEXT_NODE){
8//trimwhitespacefromcontenttext
9Stringtrimmed=child.getText().trim();
10if(trimmed.length()==0){
11//deletechildifonlywhitespace(adjustingindex)
12children.remove(i--);
13}else{
14//wrapthetrimmedcontentwithnewelement
15Elementtext=m_factory.createElement
16(QName.get("text",element.getNamespace()));
17text.addText(trimmed);
18children.set(i,text);
19}
20}elseif(child.getNodeType()==Node.ELEMENT_NODE){
21//handlechildelementswithrecursivecall
22modifyElement((Element)child);
23}
24}
25}
清单7中dom4jmodify办法与JDOM中利用的办法十分相似。欠亨过利用instanceof运算符来反省内容项的范例,我能够经由过程Node接口办法getNodeType来猎取范例代码(也能够利用instanceof,但范例代码办法看起来更明晰)。经由过程利用QName对象来暗示元素称号和经由过程挪用已保留的工场的办法来构建元素能够区分新元素的创立手艺(第15-16行)。
ElectricXML
清单8中ElectricXML(EXML)的顶级代码是任何这些示例中最复杂的一个,经由过程单一办法挪用就能够读取和编写文档。
清单8.EXML顶级代码
1//parsethedocumentfrominputstream
2Documentdoc=newDocument(in);
3//recursivelywalkandmodifydocument
4modifyElement(doc.getRoot());
5//writethedocumenttooutputstream
6doc.write(out);
清单9中EXMLmodify办法只管与JDOM一样,必要利用instanceof反省,但它与DOM办法最类似。在EXML中,没法创立一个带称号空间限制的称号的元素,以是取而代之,我创立新元素,然后设置其称号来到达不异的效果。
清单9.EXMLmodify办法
1protectedvoidmodifyElement(Elementelement){
2//loopthroughchildnodes
3Childchild;
4Childnext=element.getChildren().first();
5while((child=next)!=null){
6//setnextbeforewechangeanything
7next=child.getNextSibling();
8//handlechildbynodetype
9if(childinstanceofText){
10//trimwhitespacefromcontenttext
11Stringtrimmed=((Text)child).getString().trim();
12if(trimmed.length()==0){
13//deletechildifonlywhitespace
14child.remove();
15}else{
16//wrapthetrimmedcontentwithnewelement
17Elementtext=newElement();
18text.addText(trimmed);
19child.replaceWith(text);
20text.setName(element.getPrefix(),"text");
21}
22}elseif(childinstanceofElement){
23//handlechildelementswithrecursivecall
24modifyElement((Element)child);
25}
26}
27}
XPP
XPP的顶级代码(在清单10中)是一切示例中最长的一个,与别的模子比拟,它必要相称多的设置。
清单10.XPP顶级代码
1//parsethedocumentfrominputstream
2m_parserFactory=XmlPullParserFactory.newInstance();
3m_parserFactory.setNamespaceAware(true);
4XmlPullParserparser=m_parserFactory.newPullParser();
5parser.setInput(newBufferedReader(newInputStreamReader(in)));
6parser.next();
7XmlNodedoc=m_parserFactory.newNode();
8parser.readNode(doc);
9//recursivelywalkandmodifydocument
10modifyElement(doc);
11//writethedocumenttooutputstream
12XmlRecorderrecorder=m_parserFactory.newRecorder();
13Writerwriter=newOutputStreamWriter(out);
14recorder.setOutput(writer);
15recorder.writeNode(doc);
16writer.close();
由于利用JAXP接口,以是我必需起首创立剖析器工场的实例并在创立剖析器实例之前启用称号空间处置(第2-4行)。一旦猎取了剖析器实例,我就可以将输出设置到剖析器中,并真正构建文档暗示(第5-8行),可是这触及比别的模子更多的步骤。
输入处置(第11-16行)也触及比别的模子更多的步骤,次要由于XPP必要Writer而不是间接将Stream作为输入方针承受。
清单11中XPPmodify办法只管必要更多代码来创立新元素(第13-21行),但它与JDOM办法最相似。称号空间处置在这里有点贫苦。我起首必需创立元素的限制称号(第15-16行),然后创立元素,最初在稍后设置称号和称号空间URI(第18-21行)。
清单11.XPPmodify办法
1protectedvoidmodifyElement(XmlNodeelement)throwsException{
2//loopthroughchildnodes
3for(inti=0;i<element.getChildrenCount();i++){
4//handlechildbynodetype
5Objectchild=element.getChildAt(i);
6if(childinstanceofString){
7//trimwhitespacefromcontenttext
8Stringtrimmed=child.toString().trim();
9if(trimmed.length()==0){
10//deletechildifonlywhitespace(adjustingindex)
11element.removeChildAt(i--);
12}else{
13//constructqualifiednameforwrapperelement
15Stringprefix=element.getPrefix();
16Stringname=(prefix==null)?"text":(prefix+":text");
17//wrapthetrimmedcontentwithnewelement
18XmlNodetext=m_parserFactory.newNode();
19text.appendChild(trimmed);
20element.replaceChildAt(i,text);
21text.modifyTag(element.getNamespaceUri(),"text",name);
22}
23}elseif(childinstanceofXmlNode){
24//handlechildelementswithrecursivecall
25modifyElement((XmlNode)child);
26}
27}
28}
停止语
DOM、dom4j和ElectricXML都失掉这些几近一样易于利用的代码样本,个中EXML大概最复杂,而dom4j受一些小前提限定而较坚苦。DOM供应了与言语有关的十分其实的优点,可是假如你只利用Java代码,那末经由过程与Java特定的模子比拟较,它看上往有点贫苦。我以为这标明Java特定的模子一般乐成地完成简化Java代码中的XML文档处置这个方针。
超出基本:实在天下可用性
代码样本显现JDOM和EXML为基础文档操纵(利用元素、属性和文本)供应了复杂和明晰的接口。依据我的履历,它们的办法其实不能很好地完成处置全部文档暗示的编程义务。要完成这些范例的义务,DOM和dom4j利用的组件办法―个中附属性到称号空间的一切文档组件完成一些大众接口―事情得更好。
相干的例子是比来我为JDOM和dom4j完成的XML流型(XMLStreaming(XMLS))编码。这个代码遍历全部文档并编码每一个组件。JDOM完成比dom4j完成庞大很多,次要是由于JDOM利用一些没有大众接口的共同类来暗示每一个组件。
由于JDOM短少大众接口,以是即便处置Document对象的代码与处置Element对象的代码都有一些诸如子组件那样不异范例的组件,可是它们必需有所分歧。还必要特别办法来检索与别的范例的子组件绝对的Namespace组件。乃至当处置被以为是内容的子组件范例时,您必要在组件范例上利用多个带instanceof反省的if语句,而不是利用一条更明晰更疾速的switch语句。
具有取笑意味的多是JDOM的最后方针之一是使用JavaCollection类,这些类自己在很年夜水平上以接口为基本。库中接口的利用增添了很多天真性,而这是以增添了一些庞大性为价值的,而且这关于为重用而计划的代码来讲,一般是一个很好的折中。这大概还次要回功于dom4j,它到达一个成熟而且不乱的形态,比JDOM要快很多。
只管云云,关于利用多种言语的开辟职员来讲,DOM还是一个十分好的选择。DOM完成普遍使用于多种编程言语。它仍是很多别的与XML相干的尺度的基本,以是即便您利用Java特定的模子,也另有一个您慢慢熟习DOM所必要的好时机。由于它正式取得W3C保举(与基于非尺度的Java模子绝对),以是在某些范例的项目中大概也必要它。
就利用便利这一范围而言,在JDOM、dom4j和ElectricXML这三个次要合作者中,dom4j与别的两个的区分在于它利用带有多个承继层的基于接口的办法。这会使得遵守APIJavaDocs更加坚苦些。比方,您正在寻觅的一个办法(比方content(),在我们dom4j的modify办法示例的第3行中利用的)多是Element扩大的Branch接口的一部分,而不是Element接口自己的一部分。只管云云,这类基于接口的计划增加了很多天真性(请参阅侧栏超出基本:实在天下可用性)。思索到dom4j的功能、不乱性和特征设置的长处,您应把它看成多半项目中的一个无力的候选者。
在任一Java特定的文档模子当中,JDOM大概具有最普遍的用户基本,而且它切实其实是利用起来最复杂的模子之一。只管云云,作为项目开辟的一个选择,它仍是必需容忍API的不流动性和从一个版本到下一个版本的更新,在功能对照中它也体现得很糟。基于以后完成,我愿为动手新项目标人们保举dom4j,而不是JDOM。
除XPP之外,EXML比别的任何模子占用的资本都要少很多,而且思索到EXML易于利用的长处,您应一定会以为它合用于jar文件巨细很主要的使用程序。可是,EXML的XML撑持的范围性和受限的允许证,和在较年夜文件上所体现出的绝对低劣的功能,不能不在很多使用程序中保持利用它。
XPP在语法剖析和编写文本文档时必要更多步骤,而且在处置称号空间时也必要更多步骤。假如XPP盘算增加一些便当的办法来处置个中一些罕见情形,那末在对照中它大概会更胜一筹。正如它如今所体现的,上篇文章中功能方面的抢先者却成了本文中的可用性方面的失利者。只管云云,由于XPP功能方面的上风,以是关于必要较小的jar文件巨细的使用程序仍是值得将它作为EXML的替换办法。
下一次...
到今朝为止在我写的两篇文章中,触及到用Java编写的XML文档模子的功能和可用性。在本系列的后两篇文章中,我将会商用Java手艺举行XML数据绑定的办法。这些办法与文档模子的办法有很多类似处,可是它们更进一步将XML文档映照到实践使用程序数据布局中。我们将看到这一操纵在利用的烦琐性和进步功能方面是怎样做得云云好的。
回到developerWorks,反省Java代码的XML数据绑定的本色。同时,您能够经由过程上面链接的论坛,给出您对本文的批评和成绩。
参考材料
单击本文顶部或底部的会商,介入本文的论坛。
假如您必要背景材料,实验developerWorks的XMLJava编程教程、了解SAX教程和了解DOM教程。
从下载页面下载本文中利用的测试程序和文档模子库。
查找有关JavaAPIsforXMLProcessing(JAXP)大概浏览JAXPTutorial。
猎取作者有关XMLStreaming的著作的具体信息,它作为程序间传送XML文档的Java序列化的另外一种选择。
回忆作者之前的文章:XMLinJava:Documentmodels,part1。
依据TonyDarugar的团队对几个年夜型XML项目标剖析,参考他对EffectiveDOMwithJava的倡议。
Java的XML文档模子:
XercesJava
Crimson
JDOM
dom4j
ElectricXML
XMLPullParser(XPP)
请检察IBM的WebSphereStudioApplicationDeveloper,它是一个完成DOM的用于Java、XML和Web服务的集成可视编程情况。
想要进步您的妙技吗?请反省XML认证页―IBM专业教导企图的一部分。
那这个对象有什么意义?现在很多用javabean的人就不能保证对象有完整的意义,不成熟的使用模式等导致代码疯狂增长,调试维护的时间要得多得多。在说性能之前,先说说你这个比较的来历。据说微软为了证明。net比java好。 |
|