使用Xerces-J的DOM方式进行XML程序设计

--动态创建一个DOM树


  1. 简介与背景知识
  2. Xerces对DOM的实现
  3. 如何使用
  4. 动态创建一个DOM树
  5. 使用XMLSerializer序列化DOM对象
  6. 使用DOM的程序的实例

使用Xerces-J我们可以有两种方法动态创建DOM树,也就是说我们不需要从一个xml文本作为基础,而凭空创建一个DOM的树状模型。在后面我们还会介绍如何把这个DOM树状模型使用serialize的方式转化成xml文本。

第一种方法是使用Xerces的方法动态创建一个DOM树。这个方法不是DOM标准的,因此不能转换到其他的解析器上使用,但是功能要比标准的DOM方法强大,提供了一些很有用的功能。使用这个方法我们需要用到org.apache.xerces.dom.DocumentImpl类或者org.apache.xerces.dom.CoreDocumentImpl类。DocumentImpl类是CoreDocumentImpl类的子类,在这两个类中实现了org.w3c.dom.Document界面的全部方法,并且提供了一些其他的扩展方法。我们前面已经介绍过,Document界面是一个文档的基础,所有的内容(Element,Process Instruction, Document Type等等)都包含在一个Document中,另外,在Document界面中还包括了动态创建,增添和删除这些内容的方法。但是首先我们要创建一个Document界面的实现,才能够动态的创建这些内容,而创建一个Document界面的实现我们可以使用DocumentImpl和CoreDocumentImpl这两个类的结构函数直接建立或者说直接创建一个CoreDocumentImpl或者DocumentImpl的对象就可以了。CoreDocumentImpl类实现了核心Document的功能。而DocumentImpl类则在CoreDocumentImpl类的基础上增加了Event,Range和Traversal的功能,这些功能我们目前先不准备介绍。下面我们介绍CoreDocumentImpl的结构方法和在Document界面之外的扩展,关于如何创建Element等内容的知识请参考Document界面的介绍。

CoreDocumentImpl类的结构方法及对Document界面的扩展和对于动态创建DOM树比较重要的方法。

方法名

使用说明

CoreDocumentImpl()
缺省结构函数,但是这个不是标准DOM函数。只能在Xerces中使用。
CoreDocumentImpl(boolean grammarAccess) 这是一个结构函数。呵呵。Xerces文档上就是这么写的。好不负责任啊!
CoreDocumentImpl(DocumentType doctype) 这个方法调用DOMImplementation界面中的createDocument方法。
CoreDocumentImpl(DocumentType doctype, boolean grammarAccess) 同上。以上结构方法仅仅建立一个空的Document对象,这个对象是没有Document节点的。
Node adoptNode(Node source) 从另外一个Document对象那里获得一个子节点以及这个子节点的所有子节点,另外的Document对象不在拥有这个子节点。返回值是获得的子节点的引用。具体操作可以这样理解:首先把想要引入的子节点的ownerNode从原来的引用改成新的引用。然后把这的子节点从原来的Document中remove。最后返回这个子节点的引用。注意这里是不负责子节点的插入位置的。
Node importNode(Node source, boolean deep) 从另外一个Document对象获得一个子节点。可以选择是否同时获取这个子节点的子节点。同上面不同的是这个方法不从原来的Document中把这个子节点删除。其他同上。
void putIdentifier(java.lang.String idName, Element element)
给一个Element一个id这个方法在动态创建的DOM对象树要使用id的时候非常有用。但是这个方法不是标准的Document界面提供的方法。因此在其他的DOM实现中不一定能够使用。

下面的程序演示了如何使用这些方法以及importNode和adoptNode的不同之处。这里使用了org.apache.xml.serialize包中的内容。这些内容是为了显示xml文档而使用的,我们以后会进行介绍。

import org.apache.xerces.dom.*; import org.w3c.dom.*; import org.apache.xml.serialize.*; public class testDOM{ public static void main( String [] a ){ CoreDocumentImpl oDoc = new CoreDocumentImpl(); oDoc.setEncoding("GB2312"); Element oElem = oDoc.createElement( "hello" ); Text oText = oDoc.createTextNode("你好"); oElem.appendChild( oText ); oDoc.appendChild( oElem ); XMLSerializer oXMLS = new XMLSerializer( new OutputFormat( "XML" , "GB2312", true ) ); oXMLS.setOutputByteStream( System.out ); try{ oXMLS.serialize( oDoc ); }catch( Exception e ){ e.printStackTrace(); } oXMLS.reset(); CoreDocumentImpl oDoc2 = new CoreDocumentImpl(); Element oElem1 = oDoc2.createElement("what"); Text oText1 = oDoc2.createTextNode("Cawl"); oElem1.appendChild( oText1 ); oDoc2.appendChild( oElem1 ); oDoc2.setEncoding("UTF8"); XMLSerializer oXMLS1 = new XMLSerializer(); oXMLS1.setOutputByteStream( System.out ); try{ oXMLS1.serialize( oDoc2 ); }catch( Exception e ){ e.printStackTrace(); } oXMLS1.reset(); oDoc.putIdentifier( "1" , oElem ); Element oElemByIdDoc = oDoc.getElementById( "1"); oDoc2.putIdentifier( "1" , oElem1 ); Element oElemByIdDoc2 = oDoc2.getElementById( "1" ); try{ oXMLS1.serialize( oElemByIdDoc2 ); }catch( Exception e ){ e.printStackTrace(); } oXMLS1.reset(); Node oNode = oDoc.importNode( oElemByIdDoc2 , true ); oElemByIdDoc.appendChild( oNode ); try{ oXMLS.serialize( oDoc ); }catch( Exception e ){ e.printStackTrace(); } oXMLS.reset(); try{ oXMLS1.serialize( oDoc2 ); }catch( Exception e ){ e.printStackTrace(); } oXMLS1.reset(); oNode = null; oNode = oDoc.adoptNode( oElemByIdDoc2 ); oElemByIdDoc.appendChild( oNode ); try{ System.out.println("\nthe last trial............"); oXMLS.serialize( oDoc ); oXMLS1.serialize( oDoc2 ); }catch( Exception e ){ e.printStackTrace(); } } }

动态创建DOM树的第二种方法是采用DOM提供的标准方法。这里我们需要使用org.apache.xerces.dom.DOMImplementation类或者org.apache.xerces.dom.CoreDOMImplementation类。这两个类实现了org.w3c.dom.DOMImplementation界面。这里面最重要的一个方法就是createDocument。这个方法可以创建一个Document对象,这个方法总共有三个参数,第一个是Document节点的名空间,第二个是Document节点的qualifiedName, 第三个是DocumentType。这样,我们在调用了createDocument方法之后就已经创建了一个根节点,这一点是同我们前面介绍过的使用CoreDocumentImpl类动态创建DOM树不同的,因此需要大家注意。下面的例子演示了如何使用DOMImplementationImpl动态创建一个DOM树。

import org.apache.xerces.dom.*; import org.w3c.dom.*; import org.apache.xml.serialize.*; public class testDOM2{ public static void main( String [] a ){ CoreDOMImplementationImpl oDOM = new CoreDOMImplementationImpl(); Document oDoc = oDOM.createDocument("http://cyfer.topcool.net", "cyfer:hello", null); //oDoc.setEncoding("GB2312"); Element oElem = oDoc.getDocumentElement(); Text oText = oDoc.createTextNode("你好"); oElem.appendChild( oText ); XMLSerializer oXMLS = new XMLSerializer( new OutputFormat( "XML" , "GB2312", true ) ); oXMLS.setOutputByteStream( System.out ); try{ oXMLS.serialize( oDoc ); }catch( Exception e ){ e.printStackTrace(); } oXMLS.reset(); DOMImplementationImpl oDOM2 = new DOMImplementationImpl(); Document oDoc2 = oDOM2.createDocument("http://cyfer.topcool.net", "cyfer:what", null); Element oElem1 = oDoc2.getDocumentElement(); Text oText1 = oDoc2.createTextNode("Cawl"); oElem1.appendChild( oText1 ); //oDoc2.setEncoding("UTF8"); XMLSerializer oXMLS1 = new XMLSerializer(); oXMLS1.setOutputByteStream( System.out ); try{ oXMLS1.serialize( oDoc2 ); }catch( Exception e ){ e.printStackTrace(); } oXMLS1.reset(); Element oElemByIdDoc = oDoc.getDocumentElement( ); Element oElemByIdDoc2 = oDoc2.getDocumentElement(); try{ oXMLS1.serialize( oElemByIdDoc2 ); }catch( Exception e ){ e.printStackTrace(); } oXMLS1.reset(); Node oNode = oDoc.importNode( oElemByIdDoc2 , true ); oElemByIdDoc.appendChild( oNode ); try{ oXMLS.serialize( oDoc ); }catch( Exception e ){ e.printStackTrace(); } oXMLS.reset(); try{ oXMLS1.serialize( oDoc2 ); }catch( Exception e ){ e.printStackTrace(); } oXMLS1.reset(); /* oNode = null; oNode = oDoc.adoptNode( (Node)oElemByIdDoc2 ); oElemByIdDoc.appendChild( oNode ); try{ System.out.println("\nthe last trial............"); oXMLS.serialize( oDoc ); oXMLS1.serialize( oDoc2 ); }catch( Exception e ){ e.printStackTrace(); }*/ System.out.println( oDoc instanceof DocumentImpl ); System.out.println( oDoc2 instanceof DocumentImpl ); } }

不知道为什么,我们没有通过setEncoding和adoptNode的测试,可能是classpath上面出了问题。从上面的例子我们还可以看出,CoreDOMImplementationImpl.createDocument方法实际上创建了一个CoreDocumentImpl对象,而DOMImplementation.createDocument方法创建了一个DocumentImpl对象。这就是两者的区别。