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
置入,接受来源,一个单独的服务端包队列。