Working with OM Namespaces
由于对命名空间的处理也是解析XML的关键部分,因此AXIOM也提供了一系列API来处理命名空间:
public OMNamespace declareNamespace(String uri, String prefix);
public OMNamespace declareNamespace(OMNamespace namespace);
public OMNamespace findNamespace(String uri, String prefix) throws OMException;
以上方法对于使用其它解析方法处理过XML的朋友来说应该不难理解,但需要注意的是,一个已经添加过一次的命名空间声明不能被再次添加。
findNamespace方法是一个非常便捷的方法用来在整个对象树中找到一个命名空间对象。
在序列化过程中,一个由工厂方法直接创建的命名空间不会被立即声明,只有当序列化到这个命名空间被使用的地方(即遇到它的前缀)时才会被声明。
如果我们序列化我们创建的元素,我们会得到:
<ns:book xmlns:ns="axis2"> <ns:name></ns:name> <ns:isbn></ns:isbn> </ns:book>
Working with Attributes
下面让我们来看一下如何给book元素(代码中的root)添加一个属性:
OMAttribute type = factory.createOMAttribute("type",null, "web-services"); root.addAttribute(type);
这时我们再序列化一下代码,我们将得到:
<ns:book xmlns:ns="axis2" type="web-services"> <ns:name></ns:name> <ns:isbn></ns:isbn> </ns:book>
Traversing the AXIOM Tree
在前面的章节,我们已经学习了如何创建一个元素,如何添加子节点,如何创建命名空间和属性。现在,就让我们来遍历一下整个AXIOM树。
遍历对象结构可以通过常用的获取子节点列表的方法,但在AXIOM里,获取子节点得到的是一个iterator。下面的代码演示来如何通过给定的节点来访问子节点。子节点的类型为OMNode,可以是OMText或OMElement中的任意一种。
Iterator children = root.getChildren(); while(children.hasNext()){ OMNode node = (OMNode)children.next(); }
需要补充的是,每一个OMNode都和相邻兄弟节点相连,所以在访问相邻节点时可以使用nextSibling() 或是 PreviousSibling()方法。还可以通过直接指定字节点的名称来获取子节点,方法getChildWithName (Qname)实现来这个功能,它会返回一个iterator.使用这些iterator的优点是,整个对象结构不会立马构造,只有当需要的时候才会构造。
Serialization
到目前为止,我们对创建和遍历AXIOM树已经有了一定的了解。现在就让我们将AXIOM树序列化或是写入一个输出流。
AXIOM可以被序列化成一个纯对象模型或是一个拉事件流。序列化过程使用一个XMLStreamWriter对象来写输出,因此相同的序列化机制也可以来写不同类型的输出,比如text,binary……
AXIOM提供了个一缓存标志来控制内存中的AXIOM。OMNode提供了两个方法serializeAndConsume和serialize。当serializeAndConsume方法被调用时,缓存标志被重置,序列化器不会缓存这个流,因此如果缓存标志不被设置,这个对象模型就不会被创建。在这种情况下,XML流不会创建对象模型,而直接被序列化到输出流里。所以如果调用了serializeAndConsume方法,我们就只能序列化一次AXIOM树,因为内存中并没有生成AXIOM对象树。但是,我们可以多次调用serialize方法。文字可能不好理解,下面让我看段代码:
String xmlStream = "<ns:book xmlns:ns=\"axis2\" type=\"web-services\ "><ns:name></ns:name><ns:isbn></ns:isbn></ns:book>"; //从字符串创建一个输入流 ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream (xmlStream.getBytes()); //创建一个builder. StAXBuilder builder = new StAXOMBuilder(byteArrayInputStream); //获取root元素. OMElement root = builder.getDocumentElement(); //在这里,我们调用两次serialize root.serialize(System.out); root.serialize(System.out);
如果我们执行上面的代码,我们会在控制台看到:
<ns:book xmlns:ns="axis2" type="web-services"><ns:name></ns:name><ns: isbn></ns:isbn></ns:book> <ns:book xmlns:ns="axis2" type="web-services"><ns:name></ns:name><ns: isbn></ns:isbn></ns:book>
但是如果我们先调用serializeAndConsume()方法,再调用serialize()方法,就会出现一个异常。这是因为一旦serializeAndConsume()被调用,缓存标志就会被重置。所以紧接着调用serialize()方法时,我们没有东西可以被序列化来,因此就得到了一个异常。
//将上面的代码最后两行做如下改动,便会出现异常。
root.serializeAndConsume(System.out); root.serialize(System.out);
AXIOM and SOAP
前面已经说过,AXIOM本来是作为Axis2的一部分——它的专属XML解析器而出现的,而Axis2是一个SOAP框架,那解析SOAP消息自然也是AXIOM的主要功能之一了。SOAP也是XML,只不过有它独特的结构而已,所以我们可以很方便的从AXIOM获取一个SOAP层的API。
下面我们直接看代码,用AXIOM分别创建一个SOAP1.1和SOAP1.2文档。
Creating a SOAP 1.1 Document:
OMFactory factory = OMAbstractFactory.getOMFactory(); OMNamespace axis2 = factory.createOMNamespace("axis2", "ns"); OMElement root = factory.createOMElement("book", axis2); OMAttribute type = factory.createOMAttribute("type",null, "web-services"); root.addAttribute(type); OMElement name = factory.createOMElement("name", axis2); OMElement isbn = factory.createOMElement("isbn", axis2); root.addChild(name); root.addChild(isbn); SOAPFactory soapFactory = OMAbstractFactory.getSOAP11Factory(); //get the default envelope SOAPEnvelope env = soapFactory.getDefaultEnvelope(); //add the created child env.getBody().addChild(root); System.out.println( env);
Creating a SOAP 1.2 Document:
OMFactory factory = OMAbstractFactory.getOMFactory(); OMNamespace axis2 = factory.createOMNamespace("axis2", "ns"); OMElement root = factory.createOMElement("book", axis2); OMAttribute type = factory.createOMAttribute("type",null,"web- services"); root.addAttribute(type); OMElement name = factory.createOMElement("name", axis2); OMElement isbn = factory.createOMElement("isbn", axis2); root.addChild(name); root.addChild(isbn); SOAPFactory soapFactory = OMAbstractFactory.getSOAP12Factory(); //get the default envelope SOAPEnvelope env = soapFactory.getDefaultEnvelope(); //add the created child env.getBody().addChild(root); System.out.println( env);
两段代码唯一的区别是,使用的工厂不同而已。
Summary
通过以上的介绍,相信大家对AXIOM应该有了一个比较清晰的了解了,这样上手Axis2应该更容易了。
本AXIOM的入门连载是我学习了《Quickstart Apache Axis2》中的AXIOM一章后的一个整体理解,大部分内容为我在理解基础上的翻译。大家欲看原版文章可从附件下载。对AXIOM的理解有不对之处望直接指出,有批评才有提高嘛。