中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档 | 网通镜像
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 程序开发 > Web开发 > XML
构建自己的轻量级XMLDOM分析程序(3)
作者:yestar2000 时间:2001-12-23 11:46 出处:互联网 责编:chinaitpower
              摘要:构建自己的轻量级XMLDOM分析程序(3)
  XML语法分析策略和SimpleDOMParser实现

  与一个正常的文本文档不同的是,一个符号语法规则的XML文档有一些独特的特点,可以促进语法分析工作:

  在一个XML文档中所有的标记都必须匹配。每个起始标记都必须有一个匹配的结束标记,除了当标记本身就是两个起始和结束标记的时候,比如<parser/>就是<parser></parser>的简易格式。标记和属性名称是大小写敏感的。

  在一个XML文档中所有的标记都必须正确地嵌套。XML标记不可以交叉嵌套。 例如:一个包含<t1><t2>... </t1></t2>的文档就是错误的,因为结束标记</t1>出现在了结束标记</t2>之前。

  着眼于这些规则,SimpleDOMParser语法分析策略就应该遵循如下伪代码所示的模式:

While Not EOF(Input XML Document)

Tag = Next tag from the document

LastOpenTag = Top tag in Stack



If Tag is an open tag

Add Tag as the child of LastOpenTag

Push Tag in Stack

Else

// 结束标记

If Tag is the matching close tag of LastOpenTag

Pop Stack

If Stack is empty

Parse is complete

End If

Else

// 无效标记嵌套

Report error

End If

End If

End While

  这算法的关键就是标记堆栈,它可以保存从输入文件中获得的但是没有与它们结束标记匹配的起始标记。堆栈的顶部总是最后的一个起始标记。

  除第一个标记以外,每个新的起始标记将是上一个起始标记的子标记。所以语法分析程序把新的标记添加为上一个起始标记的子标记,然后把它推到堆栈的顶部,它就成了最新的起始标记。 另一方面,如果输入标记是一个结束标记,它必须匹配最后一个起始标记。 基于正确嵌套规则,一个不匹配结束标记会出现XML语法错误。 当结束标记匹配最后一个起始标记,语法分析程序从堆栈中弹出最后一个起始标记,因为对于这个标记的分析是完整的。这个处理继续下去,直到堆栈为空为止。此时,你就完成了整个文档的语法分析过程。代码段2给出了SimpleDOMParser.parse方法的整个源代码。

SimpleDOMParser.java

package simpledomparser;


import java.io.Reader;
import java.io.IOException;
import java.io.EOFException;
import java.util.Stack;


public class SimpleDOMParser {
private static final int[] cdata_start = {'<', '!', '[', 'C', 'D', 'A', 'T', 'A', '['};
private static final int[] cdata_end = {']', ']', '>'};

private Reader reader;
private Stack elements;
private SimpleElement currentElement;

public SimpleDOMParser() {
elements = new Stack();
currentElement = null;
}

public SimpleElement parse(Reader reader) throws IOException {
this.reader = reader;

skipPrologs();

while (true) {
int index;
String tagName;


String currentTag = readTag().trim();
if (currentTag.startsWith("</")) {
//结束标记
tagName = currentTag.substring(2, currentTag.length()-1);

//没有起始标记
if (currentElement == null) {
throw new IOException("Got close tag '" + tagName +
"' without open tag.");
}

//结束标记与起始标记不匹配
if (!tagName.equals(currentElement.getTagName())) {
throw new IOException("Expected close tag for '" +
currentElement.getTagName() + "' but got '" +
tagName + "'.");
}

if (elements.empty()) {
//文本处理结束
return currentElement;
} else {
//弹出前面的起始标记
currentElement = (SimpleElement)elements.pop();
}
} else {
// 起始标记或者带有起始标记和结束标记的标记
index = currentTag.indexOf(" ");
if (index < 0) {
// 不带属性的标记
if (currentTag.endsWith("/>")) {

tagName = currentTag.substring(1, currentTag.length()-2);
currentTag = "/>";
} else {
// 起始标记
tagName = currentTag.substring(1, currentTag.length()-1);
currentTag = "";
}
} else {
// 带有属性的标记
tagName = currentTag.substring(1, index);
currentTag = currentTag.substring(index+1);
}

//创建元素
SimpleElement element = new SimpleElement(tagName);

//分析属性
boolean isTagClosed = false;
while (currentTag.length() > 0) {

currentTag = currentTag.trim();

if (currentTag.equals("/>")) {
//结束标记
isTagClosed = true;
break;
} else if (currentTag.equals(">")) {
//起始标记
break;
}

index = currentTag.indexOf("=");
if (index < 0) {
throw new IOException("Invalid attribute for tag '" +
tagName + "'.");
}

// 取得属性名
String attributeName = currentTag.substring(0, index);
currentTag = currentTag.substring(index+1);

// 取得属性值
String attributeValue;
boolean isQuoted = true;
if (currentTag.startsWith("\"")) {
index = currentTag.indexOf('"', 1);
} else if (currentTag.startsWith("'")) {
index = currentTag.indexOf('\', 1);
} else {
isQuoted = false;
index = currentTag.indexOf(' ');
if (index < 0) {
index = currentTag.indexOf('>');
if (index < 0) {
index = currentTag.indexOf('/');
}
}
}

if (index < 0) {
throw new IOException("Invalid attribute for tag '" +
tagName + "'.");
}

if (isQuoted) {
attributeValue = currentTag.substring(1, index);
} else {
attributeValue = currentTag.substring(0, index);
}

// 添加属性到新的元素中
element.setAttribute(attributeName, attributeValue);

currentTag = currentTag.substring(index+1);
}

// 读取起始标记和结束标记之间的文本
if (!isTagClosed) {
element.setText(readText());
}

// 添加一个新的元素作为目前元素的子元素
if (currentElement != null) {
currentElement.addChildElement(element);
}

if (!isTagClosed) {
if (currentElement != null) {
elements.push(currentElement);
}

currentElement = element;
} else if (currentElement == null) {
// 在文档中只有一个标记
return element;
}
}
}
}

private int peek() throws IOException {
reader.mark(1);
int result = reader.read();
reader.reset();

return result;
}

private void peek(int[] buffer) throws IOException {
reader.mark(buffer.length);
for (int i=0; i<buffer.length; i++) {
buffer[i] = reader.read();
}
reader.reset();
}

private void skipWhitespace() throws IOException {
while (Character.isWhitespace((char)peek())) {
reader.read();
}
}

private void skipProlog() throws IOException {
//跳过"<?" or "<!"
reader.skip(2);

while (true) {
int next = peek();

if (next == '>') {
reader.read();
break;
} else if (next == '<') {

skipProlog();
} else {
reader.read();
}
}
}

private void skipPrologs() throws IOException {
while (true) {
skipWhitespace();

int[] next = new int[2];
peek(next);

if (next[0] != '<') {
throw new IOException("Expected '<' but got '" + (char)next[0] + "'.");
}

if ((next[1] == '?') || (next[1] == '!')) {
skipProlog();
} else {
break;
}
}
}

private String readTag() throws IOException {
skipWhitespace();

StringBuffer sb = new StringBuffer();

int next = peek();
if (next != '<') {
throw new IOException("Expected < but got " + (char)next);
}

sb.append((char)reader.read());
while (peek() != '>') {
sb.append((char)reader.read());
}
sb.append((char)reader.read());

return sb.toString();
}

private String readText() throws IOException {
StringBuffer sb = new StringBuffer();

int[] next = new int[cdata_start.length];
peek(next);
if (compareIntArrays(next, cdata_start) == true) {
// CDATA
reader.skip(next.length);

int[] buffer = new int[cdata_end.length];
while (true) {
peek(buffer);

if (compareIntArrays(buffer, cdata_end) == true) {
reader.skip(buffer.length);
break;
} else {
sb.append((char)reader.read());
}
}
} else {
while (peek() != '<') {
sb.append((char)reader.read());
}
}

return sb.toString();
}

private boolean compareIntArrays(int[] a1, int[] a2) {
if (a1.length != a2.length) {
return false;
}

for (int i=0; i<a1.length; i++) {
if (a1[i] != a2[i]) {
return false;
}
}

return true;
}
}

  为了简单起见,SimpleDOMParser不允许在XML文档中使用注解,并且完全忽视XML声明和DOCTYPE。它使用下面的程序来跳过XML声明和DOCTYPE元素。这个程序递归调用它本身,就像处理一个内部DTD一样处理DOCTYPE。

private void skipProlog() throws IOException {

//跳过"<?"或者 "<!"

reader.skip(2);

while (true) {

int next = peek();

if (next == '>') {

reader.read();

break;

} else if (next == '<') {

skipProlog();

} else {

reader.read();

}

}

}


  虽然本文中介绍的SimpleDOMParser只有有限的功能,但是对于许多简单的应用程序来说,它仍然是非常有用的。比如说,一个Java applet可以使用它以XML格式传送数据到一个后端服务器应用程序中。因为它是极其轻便的,所以SimpleDOMParser在资源非常有限的环境中更是很有吸引力的。此外,SimpleDOMParser的实现还很简单。虽然目前的实现只能保存元素,而不能保存声明或者DOCTYPE,但是你可以修改它来处理你想要处理的XML文本,而这一切都是非常容易的。
关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有