JDOM因其简洁易用易懂的API而被广泛的使用。JDOM常用的核心类及它们间的关系如下图所示:
Document代表了文档对象,抽象类Content表示文档中的内容元素,各种内容组成了文档对象。常用的内容元素有xml元素Element、xml注释Comment、文本Text。下面以如下片段来说明各类的含义。
<?xml version="1.0" encoding="UTF-8"?>
<customers>
<customer>
<name>徐辛波</name>
<occupation>developer</occupation>
<!-- comment:following is contact info -->
<contact>
<email>sinpo.xu@hotmail.com</email>
<mobile>15029357227</mobile>
<fix-phone>02985457683</fix-phone>
</contact>
</customer>
</customers>
上述文档用Document来抽象;customers为文档的根元素(root element ),Element即一个封闭起来的元素,element元素可以有子元素,如<mobile>15029357227</mobile>是一个元素,而<contact>...</contact>也是一个元素,甚至<customers>...</customers>也是一个大元素;<!-- ... -->代表了xml中注释,注释在JDOM中用Comment类来抽象;Text代表了xml中的文本值,如元素属性的值、元素的值、注释的内容等,父元素的Text为子元素和值组成的串,使用Text类可以方便的表示一些特殊字符,如:
Element element = new Element("name");
Text text = new Text("AAA.<、BBB/>.<CCC>");
element.addContent(text);
值得一提的是Element的方法addContent(Content content),因参数是抽象父类Content,所以可以添加Text、Element和Comment等,如果添加的是Text则自动作为element的文本值,如果是Element则作为element的子元素,如果是Comment则作为element的注释,使用十分方便。元素的值如<name>徐辛波</name>中的“徐辛波”也是一个和元素平行的Content对象(Text对象),当使用Element的getDescendants()方法时将返回一个该元素所有后代的迭代器,这些后代包括Element、Comment、Text等,如元素<contact>的后代包括email、mobile、fix-phone三个元素以及这三个元素的Text共6个后代,如果计算后代时有父子嵌套则应注意,父元素作为一个后代,其嵌套的子元素作为另一个后代。
刚才提到核心类都包含在org.jdom包下,jdom还包含了org.jdom.input和org.jdom.output两个包分别来处理xml内容的输入输出。当要读取xml资源时我们通常使用input包下的SAXBuilder类从输入流构建dom对象,当资源加载后常用的做法是在内存中缓存,这样后续的查找修改等操作就非常快。文档加载后内存的中各个元素是记录有各自的位置和关系的,即保持有上下文环境的。如果想要删除一段内容(Element Comment Text),只用调用该内容的detach方法即可,这样元素即和文档脱离关系了,再对文档进行遍历或者持久化到磁盘上时游离的元素就不可见了。Jdom的输出类包括XMLOutputter、DOMOutputter、SAXOutputter。最常用的是XMLOutputter,通过它可以将dom对象输出到指定的输出流,并且可以指定所输出xml文件的格式,比如缩进的样式等。DOMOutputter输出org.w3c.dom.Document对象,用于JDOM对象同w3c dom对象转换,SAXOutputter可以注册回调函数来处理相应的sax事件。
一下示例代码实现一个常用的读取配置文件并且允许更改后同步到磁盘的操作:
package sinpo.usagedemo;
import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.List;
import org.jdom.Document; import org.jdom.Element; import org.jdom.input.SAXBuilder; import org.jdom.output.Format; import org.jdom.output.XMLOutputter;
/** * 读取配置文件,并且修改后及时同步到磁盘 * @author 徐辛波(sinpo.xu@hotmail.com) * Oct 23, 2008 */ public class Configuration {
private Element root = null;
private Document dom = null;
private static final String resourceName = "/config.xml";
private static Configuration _INSTANCE = null;
public static synchronized Configuration getInstance() { if (_INSTANCE == null) { _INSTANCE = new Configuration(); }
return _INSTANCE; }
private Configuration() { load(); }
public String getConfig(String configName) { String configValue = null; Element found = findRecursively(configName, root); if (found != null) { configValue = found.getText(); } return configValue; }
public void updateConfig(String configName, String newValue) throws IOException { Element found = findRecursively(configName, root); if (found != null) { found.setText(newValue); } else { Element configNode = new Element(configName); configNode.addContent(newValue); // also: configNode.setText(newValue); root.addContent(configNode); } sync(); }
public void deleteConfig(String configName) throws IOException { Element found = findRecursively(configName, root); if (found != null) { found.detach(); } sync(); } private void load() { SAXBuilder builder = new SAXBuilder(); InputStream source = getClass().getResourceAsStream(resourceName); try { dom = builder.build(source); root = dom.getRootElement(); } catch (Exception e) { e.printStackTrace(); } }
// 递归查找. 在指定的父节点下查找叶子元素 private Element findRecursively(String name, Element parent) { Element found = null; List<Element> children = parent.getChildren(); if (children != null) { for (int i = 0; i < children.size(); i++) { Element element = children.get(i); String tmpName = element.getName(); if ((name.equals(tmpName)) && (!hasChild(element))) { return element; } }
for (int i = 0; i < children.size(); i++) { Element element = children.get(i); if (hasChild(element)) { found = findRecursively(name, element); if (found != null) { return found; } } } }
return found; }
private boolean hasChild(Element element) { boolean hasChild = false; List children = element.getChildren(); if ((children != null) && (children.size() > 0)) { hasChild = true; }
return hasChild; }
private void sync() throws IOException { Format format = Format.getPrettyFormat(); XMLOutputter outputter = new XMLOutputter(format); File file = null; URL url = getClass().getResource(resourceName); if (url == null) { file = new File(resourceName); } else { file = new File(url.getPath());
OutputStream out = null; try { out = new FileOutputStream(file); outputter.output(dom, out); out.close(); out = null; } catch (Exception e) { e.printStackTrace(); if (out != null) { out.close(); } } } } } |