3.3xml解析子系统
XML解析是jabber服务所做的最复杂的任务了,然而,对于java的Coder们来说,这个任务显然是小儿科,因为我们用完善的Java Sax解析库来做这些工作。我们仅仅需要对查找出处理这些的方法。
服务器的XML解析类的任务就是将xml流信息写入Packet对象,存储的PacketQueue。我们用packet和packetQueue类开始我们的xml解析过程
3.3.1描述Jabber包
jabber协议包含了客户端与服务器端交换xml碎片的办法。我们把这些xml碎片作为一个包来提交。我们把这些信息包装到java对象中,他们工作使用起来要比xml字符串简单的多。使用java对象还可以享受到java的类型检测,继承以及多态等特征的好处。
预览jabber协议就可以知道它有三个核心包需要操心:
<message></message>
,<presence></presence>
<iq></iq>
另外需要我们注意的是打开的和关闭的流标签以及流错误包:
<stream:stream>
<stream:error></stream:error>
</stream:stream>
支持这些包和标签从根本上说是很简单的。在本章最后,我们的jabber服务将能够识别和控制他们。第一步是识别他们的一般特征和封装他们到java类。
Jabber包其实是一个xml片段,因此可以把它当成一个mini的xml文档。在java中又很多的方法处理xml文档。最流行的依据w3cdom标准。大部分的javaxml解析库包含标准的支持w3cdom的java类。在dom中,xml文档时类似于树的结构体。
我们需要的xml表示不是如dom标准所能胜任的。我们知道我们接受各种各样的xml文档和我们指明的java对象。另外,不使用dom也可使我们的代码避免依附到dom库和建立服务器所需的代码。最后,我们的包类必须做比呈现xml片段更多的功能。Packets类必须能充当如下两个角色:
Pack Store——packets类是一个主要的数据储存类。信息能够被标准的java方法调用。我们能够存储包到类似w3cdom的树形数据结构。
XML writer——packet类能够知道到怎么创建自己的xml字符串表示方式。这个特征允许其他类从xml字符串呈现对象转换成的packet对象。
一个单独的xml解析类在本章最后将从接受的xml字符串转变成packet对象。Packet类的数据结构反映了xml片段的结构。例如,考虑如下xml包:
<message to='recipient'>
<subject>hello</subject>
<body>How are you doing?</body>
</message>
我们可以把它们组织到三个包中:
Packet: message (attribute to='recipient)
Packet: subject
String (value "hello")
Packet: body
String (value "How are you doing?");
在这样的数据结构中,我们能够看到一个包有一个元素名,零个或多个属性名值对,零个或多个子节。一个packet的子对象是一个字符串或其packet对象。另外,每个包有一个相关的命名空间。
作为一个java类,我们能够存储packet的子节到java.util.List类型的List中。他的属性在java.util.Hashtable,并且其他值是字符串。如果这个包没有父节点,包的parent值是null。
这个类包括三个构造器:
The Packet class constructors
public class Packet {
public Packet(String element){
setElement(element);
}
public Packet(String element, String value){
setElement(element);
children.add(value);
}
public Packet(Packet parent,
String element,
String namespace,
Attributes atts){
setElement(element);
setNamespace(namespace);
setParent(parent);
//Copy attributes into hashtable
for (int i = 0; i < atts.getLength(); i++){
attributes.put(atts.getQName(i), atts.getValue(i));
}
}
parent和children成员变来那个负责packet的树形结构和他们的子节。我们能够用LinkedList类型的类存储packet和Strings。另外,packet的命名空间和元素名必须被存储成字符串。
String namespace;
public void setNamespace(String name) { namespace = name; }
public String getNamespace() { return namespace; }
String element;
public void setElement(String element) { this.element = element; }
public String getElement() { return element; }
Packet parent;
public Packet getParent() { return parent; }
public void setParent(Packet parent){
this.parent = parent;
if (parent != null){
parent.children.add(this);
}
}
LinkedList children = new LinkedList();
public LinkedList getChildren() {return children;}
packet提供了几个构造器。假设我们用个如下的xml包:
<item>
ItemValue
<sub-item>sub-item value</sub-item>
<sub-item>another value</sub-item>
</item>
有三个典型的典型任务,其他的类将在packet中执行:
l 获取给定元素名的子包。(例如:<sub-item>sub-item value</sub-item>).
l 获取packet的第一个字符串值。
l 获取字符串值相关的第一个子包。
第一个方法是从一个给定的元素名定位第一个子包。他在getFirstChild()中实现。例如,考虑最近的<itme>xml包。你能够在itme包对象中调用getFirstChild(“sub-itme”),得到元素名为sub-item的子包。
public Packet getFirstChild(String subelement){
Iterator childIterator = children.iterator();
while (childIterator.hasNext()){
Object child = childIterator.next();
if (child instanceof Packet){
Packet childPacket = (Packet)child;
if (childPacket.getElement().equals(subelement)) {
return childPacket;
}
}
}
return null;
}
另外一些基本的任务是依据一个元素获得字符串值。例如,我们得到了<sub-item>子包,我们能够知道他的值(“sub-item value”)。你能够通过调用<sub-item>的包对象getValue()方法得到。
public String getValue(){
StringBuffer value = new StringBuffer();
Iterator childIterator = children.iterator();
while (childIterator.hasNext()){
Object valueChild = childIterator.next();
if (valueChild instanceof String){
value.append((String)valueChild);
}
}
return value.toString().trim();
}
甚至有许多类似的情况,当我们想知道sub-packet的子节的字符串值。再上一个例子中,我们通过调用子包的getFirstChild(“sub-item”),得到<sub-item>的子包值。然后调用getValue()得到他的字符串值。有了这些方便的方法,我们能够更方便的结合到getChildValue()方法中。public String getChildValue(String subelement){
Packet child = getFirstChild(subelement);
if (child == null){
return null;
}
return child.getValue();
}
输入包经常依赖于会话上下文和其它动作路由。每一个在我们的jabber服务中的client或server能够有一个相关的session对象出处session上下文。这个packet存储这个会话对象的引用。
Session session;
public void setSession(Session session) { this.session = session; }
public Session getSession() {
if (session != null){
return session;
}
if (parent != null){
return parent.getSession();
}
return null;
}
许多jabber协议依赖于packet属性翻译包相关信息和他的动作。Packet类存储属性到java.util.Hashtable。另外,他提供了几个方便的方法访问大部分的一般packet属性:
l to——包的接受对象
l from——包发送者
l id——包ID,唯一标识包
l type——包类型。依据协议而定。
Hashtable attributes = new Hashtable();
public String getAttribute(String attribute) {
return (String)attributes.get(attribute);
}
public void setAttribute(String attribute, String value) {
if (value == null){
removeAttribute(attribute);
} else {
attributes.put(attribute,value);
}
}
public void removeAttribute(String attribute){
attributes.remove(attribute);
}
public void clearAttributes(){
attributes.clear();
}
public String getTo() { return (String)attributes.get("to"); }
public void setTo(String recipient) { setAttribute("to",recipient); }
public String getFrom() { return (String)attributes.get("from"); }
public void setFrom(String sender){ setAttribute("from",sender); }
public String getType() { return (String)attributes.get("type"); }
public void setType(String type){ setAttribute("type",type); }
public String getID() { return (String)attributes.get("id"); }
public void setID(String ID) { setAttribute("id",ID); }
}
最后,packet类能够把它自己写成一个xml字符串,依靠java.io.Writer。创建xml呈现的过程包括遍历树顺序,输出合适的元素,属性,以及子节。
public void writeXML() throws IOException {
writeXML(session.getWriter());
}
public void writeXML(Writer out) throws IOException{
out.write("<");
out.write(element);
//Output the attributes for the element
Enumeration keys = attributes.keys();
while (keys.hasMoreElements()){
String key = (String)keys.nextElement();
out.write(" ");
out.write(key);
out.write("='");
out.write((String)attributes.get(key));
out.write("'");
}
//Empty element
if (children.size() == 0){
out.write("/>");
out.flush();
return;
}
out.write(">");
//Iterate over each child
Iterator childIterator = children.iterator();
while (childIterator.hasNext()){
Object child = childIterator.next();
//Send value to Writer
if (child instanceof String){
out.write((String)child);
//Or recursively write its children's XML
} else {
((Packet)child).writeXML(out);
}
}
out.write("</");
out.write(element);
out.write(">");
out.flush();
}
public String toString(){
try {
StringWriter reply = new StringWriter();
writeXML(reply);
return reply.toString();
} catch (Exception ex){
}
return "<" + element + ">";
}
}
packet置入,接受来源,一个单独的服务端包队列。