网站首页
JSP空间
动态资讯
开源项目
技术文档
资源下载
J2EE资源
客户论坛
在线支付
 
  技术文档>>JAVA>>新手入门>>基础入门>查看文档  
  创造一种迅速而又随性的(quick and dirty)xml解释器     
  文章作者:未知  文章来源:水木森林  
  查看:113次  录入:管理员--2007-11-17  
 
  from javaworld.com 翻译by matrix开源技术-chris

xml是一种当前很受欢迎的数据格式, 它的优点在于: 人性化,自述性以及使用的方便性.但是,不幸的是,基于java的xml解释器往往太大了,比如sun的jaxp.jar 和 parser.jar 每个都达到了1.4mb. 如果你要在只有有限的内存容量的运行环境里运行你的程序,比如j2me的环境.或者说带宽很有限的运行环境里,比如applet,这些大的package不应该成为你的选择对象.
注意:本篇的所有所需要的所有代码你可以通过此链接下载:
http://www.matrix.org.cn/down_view.asp?id=67
下面是qdparser的代码:
package qdxml;
import java.io.*;
import java.util.*;

/** quick and dirty xml parser. this parser is, like the sax parser,
an event based parser, but with much less functionality. */
public class qdparser {
private static int popmode(stack st) {
if(!st.empty())
return ((integer)st.pop()).intvalue();
else
return pre;
}
private final static int
text = 1,
entity = 2,
open_tag = 3,
close_tag = 4,
start_tag = 5,
attribute_lvalue = 6,
attribute_equal = 9,
attribute_rvalue = 10,
quote = 7,
in_tag = 8,
single_tag = 12,
comment = 13,
done = 11,
doctype = 14,
pre = 15,
cdata = 16;
public static void parse(dochandler doc,reader r) throws exception {
stack st = new stack();
int depth = 0;
int mode = pre;
int c = 0;
int quotec = ´"´;
depth = 0;
stringbuffer sb = new stringbuffer();
stringbuffer etag = new stringbuffer();
string tagname = null;
string lvalue = null;
string rvalue = null;
hashtable attrs = null;
st = new stack();
doc.startdocument.);
int line=1, col=0;
boolean eol = false;
while((c = r.read()) != -1) {

// we need to map , , and to
// see xml spec section 2.11
if(c == ´´ && eol) {
eol = false;
continue;
} else if(eol) {
eol = false;
} else if(c == ´´) {
line++;
col=0;
} else if(c == ´´) {
eol = true;
c = ´´;
line++;
col=0;
} else {
col++;
}

if(mode == done) {
doc.enddocument.);
return;

// we are between tags collecting text.
} else if(mode == text) {
if(c == ´<´) {
st.push(new integer(mode));
mode = start_tag;
if(sb.length() > 0) {
doc.text(sb.tostring());
sb.setlength(0);
}
} else if(c == ´&´) {
st.push(new integer(mode));
mode = entity;
etag.setlength(0);
} else
sb.append((char)c);

// we are processing a closing tag: e.g. </foo>
} else if(mode == close_tag) {
if(c == ´>´) {
mode = popmode(st);
tagname = sb.tostring();
sb.setlength(0);
depth--;
if(depth==0)
mode = done;
doc.endelement(tagname);
} else {
sb.append((char)c);
}

// we are processing cdata
} else if(mode == cdata) {
if(c == ´>´
&& sb.tostring().endswith("]]")) {
sb.setlength(sb.length()-2);
doc.text(sb.tostring());
sb.setlength(0);
mode = popmode(st);
} else
sb.append((char)c);

// we are processing a comment. we are inside
// the <!-- .... --> looking for the -->.
} else if(mode == comment) {
if(c == ´>´
&& sb.tostring().endswith("--")) {
sb.setlength(0);
mode = popmode(st);
} else
sb.append((char)c);

// we are outside the root tag element
} else if(mode == pre) {
if(c == ´<´) {
mode = text;
st.push(new integer(mode));
mode = start_tag;
}

// we are inside one of these <? ... ?>
// or one of these <!doctype ... >
} else if(mode == doctype) {
if(c == ´>´) {
mode = popmode(st);
if(mode == text) mode = pre;
}

// we have just seen a < and
// are wondering what we are looking at
// <foo>, </foo>, <!-- ... --->, etc.
} else if(mode == start_tag) {
mode = popmode(st);
if(c == ´/´) {
st.push(new integer(mode));
mode = close_tag;
} else if (c == ´?´) {
mode = doctype;
} else {
st.push(new integer(mode));
mode = open_tag;
tagname = null;
attrs = new hashtable();
sb.append((char)c);
}

// we are processing an entity, e.g. <, », etc.
} else if(mode == entity) {
if(c == ´;´) {
mode = popmode(st);
string cent = etag.tostring();
etag.setlength(0);
if(cent.equals("lt"))
sb.append(´<´);
else if(cent.equals("gt"))
sb.append(´>´);
else if(cent.equals("amp"))
sb.append(´&´);
else if(cent.equals("quot"))
sb.append(´"´);
else if(cent.equals("apos"))
sb.append(´´´);
// could parse hex entities if we wanted to
//else if(cent.startswith("#x"))
//sb.append((char)integer.parseint(cent.substring(2),16));
else if(cent.startswith("#"))
sb.append((char)integer.parseint(cent.substring(1)));
// insert custom entity definitions here
else
exc("unknown entity: &"+cent+";",line,col);
} else {
etag.append((char)c);
}

// we have just seen something like this:
// <foo a="b"/
// and are looking for the final >.
} else if(mode == single_tag) {
if(tagname == null)
tagname = sb.tostring();
if(c != ´>´)
exc("expected > for tag: <"+tagname+"/>",line,col);
doc.startelement(tagname,attrs);
doc.endelement(tagname);
if(depth==0) {
doc.enddocument.);
return;
}
sb.setlength(0);
attrs = new hashtable();
tagname = null;
mode = popmode(st);

// we are processing something
// like this <foo ... >. it could
// still be a <!-- ... --> or something.
} else if(mode == open_tag) {
if(c == ´>´) {
if(tagname == null)
tagname = sb.tostring();
sb.setlength(0);
depth++;
doc.startelement(tagname,attrs);
tagname = null;
attrs = new hashtable();
mode = popmode(st);
} else if(c == ´/´) {
mode = single_tag;
} else if(c == ´-´ && sb.tostring().equals("!-")) {
mode = comment;
} else if(c == ´[´ && sb.tostring().equals("![cdata")) {
mode = cdata;
sb.setlength(0);
} else if(c == ´e´ && sb.tostring().equals("!doctyp")) {
sb.setlength(0);
mode = doctype;
} else if(character.iswhitespace((char)c)) {
tagname = sb.tostring();
sb.setlength(0);
mode = in_tag;
} else {
sb.append((char)c);
}

// we are processing the quoted right-hand side
// of an element´s attribute.
} else if(mode == quote) {
if(c == quotec) {
rvalue = sb.tostring();
sb.setlength(0);
attrs.put(lvalue,rvalue);
mode = in_tag;
// see section the xml spec, section 3.3.3
// on normalization processing.
} else if(" u0009".indexof(c)>=0) {
sb.append(´ ´);
} else if(c == ´&´) {
st.push(new integer(mode));
mode = entity;
etag.setlength(0);
} else {
sb.append((char)c);
}

} else if(mode == attribute_rvalue) {
if(c == ´"´ || c == ´´´) {
quotec = c;
mode = quote;
} else if(character.iswhitespace((char)c)) {

} else {
exc("error in attribute processing",line,col);
}

} else if(mode == attribute_lvalue) {
if(character.iswhitespace((char)c)) {
lvalue = sb.tostring();
sb.setlength(0);
mode = attribute_equal;
} else if(c == ´=´) {
lvalue = sb.tostring();
sb.setlength(0);
mode = attribute_rvalue;
} else {
sb.append((char)c);
}

} else if(mode == attribute_equal) {
if(c == ´=´) {
mode = attribute_rvalue;
} else if(character.iswhitespace((char)c)) {

} else {
exc("error in attribute processing.",line,col);
}

} else if(mode == in_tag) {
if(c == ´>´) {
mode = popmode(st);
doc.startelement(tagname,attrs);
depth++;
tagname = null;
attrs = new hashtable();
} else if(c == ´/´) {
mode = single_tag;
} else if(character.iswhitespace((char)c)) {

} else {
mode = attribute_lvalue;
sb.append((char)c);
}
}
}
if(mode == done)
doc.enddocument.);
else
exc("missing end tag",line,col);
}
private static void exc(string s,int line,int col)
throws exception
{
throw new exception(s+" near line "+line+", column "+col);
}
}
为何不使用sax?
你可以实现仅还有有限功能的sax接口, 当遇到某些东西你不需要的时候,抛出notimplemented异常.
无庸置疑地, 这样你可以开发出小于jaxp.jar和parser.jar的类.但是,你可以通过定义自己的类来达到更加小的size.实际上,我们这里定义的类将会比sax接口还要小很多.
我们的迅速而又随性的xml解释器有点类似于sax. 类似于sax解释器,它能够让你实现接口从而可以捕获并处理与属性和开始/结束标签. 你们如果已经使用过sax,你们会发现它很熟悉.

限制的xml功能
很多人都喜欢xml样式的简单的,自述的,文本式的数据格式. 他们希望很容易地获取当中地元素,属性以及属性的值. 顺着这种思想,让我们来考虑一下哪些功能使我们必须的.
我们的简单的解释器只有一个类:qdparser 与一个接口:dochandler. qdparser拥有一个public的静态方法-parse(dochandler,reader)?我们把它定义成一个有限状态自动机.
我们的简单的解释器会把dtd <!doctype> 与 <?xml version="1.0"?>仅仅看成是注释,所以,他们不会造成混乱,他们的内容对我们来说也是无用的.
因为我们不能处理doctype, 我们的解释器不能读取自定义的实体.只有这些是作为标准可用的: &, <, >, ', and ".如果你觉得这些不够,那么,可以自己插入代码来扩展自己的定义.或者你也可以再递交给qdparser之前先预处理你的xml文件.
我们的简单的解释器也不支持条件选择:比如, <![include[ ... ]]> or <![ignore[ ... ]]>.因为我们不能通过doctype自定义实体,这个功能对我们来说也是毫无意义的.我们可以在数据传递到我们的有限容量处理设备之前解决这个条件选择的问题.
因为我们的解释器不会处理任何属性的声明,xml规范要求我们把所有的数据类型都看成是cdata,这样,我们可以使用java.util.hashtable来代替org.xml.sax.attributelist来存储一个元素的属性列表.在hashtable里,我们仅仅有名字/值对应的信息,因为我们不需要gettype()因为此时,无论如何都会返回cdata.
缺少属性声明会导致一些其他的结果,比如,解释器不提供默认的属性值.还有,我们也不能通过声明nmtokens来自动减少空闲空间.然后,这些都可以在我们准备或者生成xml文件的时候处理.这些额外的代码都可以放到使用我们的parser的程序外部去.
实际上,缺少的功能都可以在准备xml文件的时候补偿回来,这样,你就可以分担很多功能-我们的parser失去的功能 给准备xml文件的时候处理.

解释器功能
既然讨论了这么多我们的parser不能做到的事情,那什么是它可以做到的呢?
1. 它能识别所有元素的开始和结束标签.
2. 它能够列出所有属性.
3. 它能够识别<[cdata[ ... ]]> 这种结构
4. 它能够识别标准实体: &, <, >, ", and &apos,与数字实体.
5. 它能将输入的和 to 看成是一行的结束,符合xml规范里的2.11.
这个解释器仅仅带有很有限的错误检查,当遇到错误的文法的时候,就会抛出异常,比如遇到它不能识别的实体.

如何使用这个解释器
使用我们这个quick and dirty解释器是很简单的,首先,实现dochandler的接口,然后就可以解释一个xml文件:
dochandler doc = new mydochandler();
qdparser.parse(doc,new filereader("config.xml"));
源代码包含有两个实现了全部dochandler接口的例子,第一个叫
reporter,仅仅是输出它读到的内容,你可以用例子里的xml文件:config.xml来测
试这个例子.
第二个例子conf稍微复杂,conf实现更新已经存在的驻扎的内存的数据.
conf通过java.lang.reflect来定位config.xml里定义的fields和对象.如果你运行这个程序,它会告诉你哪些对象在更新与如何更新.如果遇到要求更新不存在的fields,它会报出错信息.

修改这个package
你可以修改这个类来使之适合你自己的使用,你可以添加你自定义的实体定义-在qdparser.java的第180行.
你也可以添加我排除的功能到这个解释器的有限状态机里面去.这个是比较容易实现的,因为原有的代码量很少.

keep it small
qdparser只有3kb大小当你编译之后或者打包到jar文件里去.源代码也只有300行,还包括注释在内,这个对很多小容量的设备来说是有效的,可以保持符合xml的标准,并实现基本的功能.
 
 
上一篇: 创建接受格式化文本的输入域    下一篇: 从.class文件中寻找类名
  相关文档
jdbc接口技术介绍1 11-17
java.util包学习笔记一 11-17
spring + hibernate 数据话持久层(转) 11-17
j2me开发入门专题系列之二:j2me应用程序 11-17
读写指定的属性文件演示 11-17
关于java连接各种数据库的实例 11-16
int与bytearray之间的转换程序 11-17
java 2d 功能概述 11-17
java rmi and .net remoting 11-17
hibernate入门 > jbx + sqlserver2k 快速上手例子 11-17
digester 解析 xml 文档 sample 11-17
在java开源的王国中自由翱翔 11-16
java初学者应该搞懂的六个问题 11-17
java新学法之robocode基本原理之坐标锁定(下) 11-17
java socket编程(五) 11-17
如何成为一个成功的jsp程序员?(zt) 11-17
java语言深入--关于java语言的内存泄漏 03-06
利用socket进行java网络编程 11-16
java gui:swt/swing/awt的介绍及比较 11-16
字符终端的管理 11-17
返回首页 | 关于我们 | J网章程 | JSP空间合租 | 客服中心 | 免责声明 | 常见问题 | 参观机房
本站主机空间代理至厦门市华众网络科技有限公司
《中华人民共和国增值电信业务经营许可证》
编号:闽B2-20050079
@2005-2008福建JSP技术网 版权所有 闽ICP备05000928号
技术电话:13616026886
邮箱:admin@fjjsp.com 站长QQ,点击这里给我发消息