一个用dom编写的目录树形结构,请大家提提意见。

目录可以在任何层次添加、删除。

category.dtd


<?xml version="1.0" encoding="GBK"?>
<!ELEMENT category ( category*)>
<!ATTLIST category
id ID REQUIRED
name CDATA REQUIRED
locked (true|false)
"false"
disvisible (true|false)
"false"
level CDATA REQUIRED
>

category.xml


<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE category SYSTEM
"category.dtd">
<category id=
"c0" name="category0" locked="false" disvisible="false" level="0">
<category id=
"c1" name="category1" locked="false" disvisible="false" level="1"/>
</category>

CategoryDetail


import java.util.List;

public class CategoryDetail {
private String id;
private String name;
private boolean locked;
private boolean disvisible;
private int level;
private List parentList;//所有父亲的集合,按继承顺序排列。
private List childList;
//所有孩子的集合

public CategoryDetail(String id, String name, boolean locked, boolean disvisible, int level, List parentList, List childList) {
this.id = id;
this.name = name;
this.locked = locked;
this.disvisible = disvisible;
this.level = level;
this.parentList = parentList;
this.childList = childList;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public boolean isLocked() {
return locked;
}

public void setLocked(boolean locked) {
this.locked = locked;
}

public boolean isDisvisible() {
return disvisible;
}

public void setDisvisible(boolean disvisible) {
this.disvisible = disvisible;
}

public int getLevel() {
return level;
}

public void setLevel(int level) {
this.level = level;
}

public List getParentList() {
return parentList;
}

public void setParentList(List parentList) {
this.parentList = parentList;
}

public List getChildList() {
return childList;
}

public void setChildList(List childList) {
this.childList = childList;
}

public String printTag(int count,String tag){
StringBuffer sb=new StringBuffer();
for(int i=0;i<count;i++){
sb.append(tag);
}
return sb.toString();
}

}

CategoryData


public class CategoryData {
public static CategoryData Empty=new CategoryData();
private String id;
private String name;
private boolean locked;
private boolean disvisible;
private int level;

public CategoryData() {
}

public CategoryData(String id, String name, boolean locked, boolean disvisible, int level) {
this.id = id;
this.name = name;
this.locked = locked;
this.disvisible = disvisible;
this.level = level;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public boolean isLocked() {
return locked;
}

public void setLocked(boolean locked) {
this.locked = locked;
}

public boolean isDisvisible() {
return disvisible;
}

public void setDisvisible(boolean disvisible) {
this.disvisible = disvisible;
}

public int getLevel() {
return level;
}

public void setLevel(int level) {
this.level = level;
}

public String toString() {
return "CategoryData: id="+id+" name="+name;
}
}

CategoryTree


import org.w3c.dom.*;

import javax.xml.parsers.*;
import java.io.*;
import java.util.*;

public class CategoryTree {
private Document doc;
private String xmlFile = "D:\\myapp\\xmltree\\build\\category.xml";
private final static String ID_ATTR =
"id";
private final static String NAME_ATTR =
"name";
private final static String LOCKED_ATTR =
"locked";
private final static String DISVISIBLE_ATTR =
"disvisible";
private final static String LEVEL_ATTR =
"level";
private final static String CATEGORY_ELEMENT =
"category";

public CategoryTree() {
load();
}

public CategoryDetail getCategoryDetail(String id) throws CategoryNotFoundException {
Element element = doc.getElementById(id);
if (element == null)
throw new CategoryNotFoundException();
return new CategoryDetail(
id,
element.getAttribute(NAME_ATTR),
Boolean.getBoolean(element.getAttribute(LOCKED_ATTR)),
Boolean.getBoolean(element.getAttribute(DISVISIBLE_ATTR)),
Integer.parseInt(element.getAttribute(LEVEL_ATTR)),
getParentList(element),
getChildList(element)
);
}

public CategoryData getCategoryData(String id) throws CategoryNotFoundException {
Element element = doc.getElementById(id);
if (element == null)
throw new CategoryNotFoundException();
else
return new CategoryData(
id,
element.getAttribute(NAME_ATTR),
Boolean.getBoolean(element.getAttribute(LOCKED_ATTR)),
Boolean.getBoolean(element.getAttribute(DISVISIBLE_ATTR)),
Integer.parseInt(element.getAttribute(LEVEL_ATTR))
);
}

public void createCategory(String name, String parentID) throws CategoryNotFoundException {
Element parent = doc.getElementById(parentID);
if (parent == null)
throw new CategoryNotFoundException();
int parentLevel = Integer.parseInt(parent.getAttribute(LEVEL_ATTR));
Element element = doc.createElement(CATEGORY_ELEMENT);
//测试时,用时间作ID.
element.setAttribute(ID_ATTR,
"c" + String.valueOf(System.currentTimeMillis()));
element.setAttribute(NAME_ATTR, name);
element.setAttribute(LOCKED_ATTR,
"true");
element.setAttribute(DISVISIBLE_ATTR,
"true");
element.setAttribute(LEVEL_ATTR, String.valueOf(++parentLevel));
parent.appendChild(element);
store();
}

public void removeCategory(String id) throws CategoryNotFoundException {
Element element = doc.getElementById(id);
if (element == null)
throw new CategoryNotFoundException();
Node parent = element.getParentNode();
parent.removeChild(element);

store();
}

private void load() {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(true);
factory.setNamespaceAware(false);
factory.setIgnoringElementContentWhitespace(true);
DocumentBuilder builder = factory.newDocumentBuilder();
doc = builder.parse(new File(xmlFile));
} catch (Exception e) {
e.printStackTrace();
System.out.println(
"can not load xml resource.");
}
}

private void store() {
try {
//实现一个DOMSerializer类,来存储xml。
DOMSerializer serializer = new DOMSerializer(
"GBK");
serializer.serialize(doc, new File(xmlFile));
} catch (Exception e) {
e.printStackTrace();
}
}


private List getChildList(Element element) {
List childList = new ArrayList();
NodeList nodeList = element.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element child = (Element) node;
childList.add(
new CategoryData(
child.getAttribute(ID_ATTR),
child.getAttribute(NAME_ATTR),
Boolean.getBoolean(child.getAttribute(LOCKED_ATTR)),
Boolean.getBoolean(child.getAttribute(DISVISIBLE_ATTR)),
Integer.parseInt(child.getAttribute(LEVEL_ATTR))
)
);
}
}
return childList;
}

private List getParentList(Element element) throws CategoryNotFoundException {
int level = Integer.parseInt(element.getAttribute(
"level"));
LinkedList parentList = new LinkedList();
Element tmp = null;
for (int i = 0; i < level; i++) {
tmp = (Element) element.getParentNode();
parentList.addFirst(getCategoryData(tmp.getAttribute(
"id")));
element = tmp;
}
return parentList;
}

public static void main(String[] args) throws Exception {

CategoryTree tree = new CategoryTree();
long start = System.currentTimeMillis();
tree.createCategory(
"我的文档", "c0");
tree.removeCategory(
"c3");
CategoryDetail c = tree.getCategoryDetail(
"c0");


Iterator iter = c.getChildList().iterator();
while (iter.hasNext()) {
System.out.println(iter.next());
}

long end = System.currentTimeMillis() - start;
System.out.println(
"run :" + end + " ms");
}
}

应该跟用户权限结合起来。
比如权限分配,只能由administrator看见。

这只是一个简单的演示。
如果要加权限,可以直接在可以加上readRole,addRole,adminRole等属性,就可以了。

感觉用dom解析,速度慢了点,占内存。
还是应该用数据库来产生目录的xml文档。然后在页面格式化显示xml文档的速度快。

非常不错

XML实现树形结构有其优势,但是很多情况下,我们并不需要知道整个树形结构,因为整个树形结构显示出来也许非常复杂,一般只关系某个层次的父子、兄弟关系。

树形结构比较麻烦的是数据库节点实现和前台页面节点管理相结合。
使用数据库节点管理的好处是可以接受非常大的树结构数据。

节点如何和资源挂钩,也是一个比较麻烦的事情。

好像jspsmartmenu也是根据xml生成的吧。

我现在的开发的系统就是用数据库的,我想改成xml的。

你的程序最好不要用File IO来读写硬盘文件,这样你必须在程序里面写死一个绝对路径名称,一旦换台机器部署,就必须修改源代码。改用Classloader来查找文件,例如:


FileInputStream fin = getClass().getClassLoader().getResourceAsStream("/category.xml");

这样,只要在CLASSPATH下的文件,就一定会被搜索到。

其实我不明白你为什么要用DOM来操纵XML,如果改用dom4j,很简单的几行的代码就可以了,完全不需要这么麻烦。

说到性能,用DOM来操纵XML,一方面取决于使用的XML Parser的性能,另一方面取决于API的性能,JDK自带的Crimson的性能很差,已经被Apache的XML项目小组淘汰掉了,如果改用Xerces2,性能会高很多,这需要配置一下JRE\lib\jaxp.properties文件。

推荐一篇关于Java XML API的性能和易用性的很棒的文章:
http://www-900.ibm.com/developerWorks/cn/xml//x-injava/index.shtml
http://www-900.ibm.com/developerWorks/cn/xml/x-injava2/index.shtml

用jdom也很简单吧,而且网上资料也很多。

你看看我给出的URL连接,jdom的性能奇差。jdom不论性能还是实现的功能都无法和dom4j想比,有趣的是,连Sun发布的jaxm也使用了dom4j。

指出一点小小的问题

FileInputStream fin = getClass().getClassLoader().getResourceAsStream("/category.xml");

这个方法会根据容器的不同实现有不同的反应

最好在自己对其classloader比较熟悉的容器上使用,以避免不必要的麻烦

嗯,确实应该稍微注意,不过问题也不大。因为ClassLoader继承是有层次的,只要你把xml只放到系统环境变量CLASSPATH指定的路径下,由App ClassLoader来搜索,那么不管什么容器都不会有问题了。

用jaxb好了

在实际开发中,那些模块常用xml?

利用DOM来解析XML的话需要考虑到数据文件的大小。当XML文件在1兆以上的时候DOM的性能将会急剧的下降。

这也是为什么SAX/SAX2能够得到广泛使用的原因之一了。

FileInputStream fin = getClass().getClassLoader().getResourceAsStream("/category.xml");
其实也不见得好
既然都有需要经常改变的菜单了
自然这个系统就有自己的配置文件,配置文件指明路径就可以了
至于配置文件在哪,大可以写进web.xml,如struts的web.xml配置一样

对了,我们这里也是用xml来维护一个菜单文件的,比较长了,有简单的权限等。建议如果不是做通用系统,还是实现一个适合自己的就好了