使用dom方式操作xml文件,即是和dom树打交道的过程:在构建xml文件时,首先构建一棵dom树,然后将该树状结构写成xml文件;在解析xml文件时,首先将源xml文件解析成一棵dom树,然后遍历这棵dom树、或从dom树中查找需要的信息。
关于dom树中节点类型、不同节点具有的接口、特性、限制等信息可以参考《dom树节点解析》,本文只关注如何构建xml文件与解析xml文件。在构建和解析xml文件中,都以w3school中的books.xml文件的内容为例:
<?xml version="1.0" encoding="utf-8"?>
<bookstore>
<book category="children">
<title lang="en">harry potter</title>
<author>j k. rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="cooking">
<title lang="en">everyday italian</title>
<author>giada de laurentiis</author>
<price>30.00</price>
<bookcategory="web"cover="paperback" >
<title lang="en">learning xml</title>
<author>erik t. ray</author>
<year>2003</year>
<price>39.95</price>
<book category="web">
<title lang="en">xquery kick start</title>
<author>james mcgovern</author>
<author>per bothner</author>
<author>kurt cagle</author>
<author>james linn</author>
<author>vaidyanathan nagarajan</author>
<price>49.99</price>
</bookstore>
我们都知道java是一门面向对象的语言,因而我们需要尽量以面向对象的思想我编写代码,面向对象编程其中一个比较重要的特点就是基于对象编程,因而我们在编写这个测试代码时,也尽量的基于对象操作,而不是像过程式的语言,有一点信息做一点操作。
在这里,对xml文件中定义的book元素,我们使用book对象与其对应:
public class book {
private string category;
private string cover;
private titleinfo title;
private list<string> authors;
private int year;
private double price;
...
public static class titleinfo {
private string title;
private string lang;
...
}
}
根据xml文件定义构建book实例:
public class w3cbooksbuilder {
public static list<book> buildbooks() {
list<book> books = new arraylist<book>();
books.add(buildharraybook());
books.add(builceverydayitalian());
books.add(buildlearningxml());
books.add(buildxquerykickstart());
return books;
public static book buildharraybook() {
book book = new book();
book.setcategory("children");
book.settitle(new titleinfo("harry potter", "en"));
book.setauthors(arrays.aslist("j k. rowling"));
book.setyear(2005);
book.setprice(29.99);
return book;
public static book builceverydayitalian() {
...
public static book buildlearningxml() {
public static book buildxquerykickstart() {
dom使用documentbuilder类来解析xml文件,它提供parse方法,将xml文件解析成一棵dom树,并返回document实例:
public document parse(inputstream is);
public document parse(inputstream is, string systemid);
public document parse(string uri);
public document parse(file f);
public abstract document parse(inputsource is);
documentbuilder类还提供了判断当前解析器是否存在命名空间解析、验证等配置,以及提供了设置entityresolver、errorhandler的接口。这里使用entityresolver和errorhandler只是重用sax的api,并不表示dom解析的内部实现一定要基于sax,然而貌似jdk自带的dom解析内部使用的引擎就是sax。t_t
public abstract boolean isnamespaceaware();
public abstract boolean isvalidating();
public abstract void setentityresolver(entityresolver er);
public abstract void seterrorhandler(errorhandler eh);
documentbuilder提供了 构建document实例的工厂方法,在以编程方式构建dom树时,首先需要构建document实例,继而使用document实例构建其余节点类型,而构建document实例需要通过documentbuilder类来实现:
public abstract document newdocument();
最后,documentbuilder还提供了一些额外的方法,比如重置documentbuilder实例的状态,以重用该documentbuilder;获取domimplementation实例;获取schema实例;判断xinclude处理模式。
public void reset();
public abstract domimplementation getdomimplementation();
public schema getschema();
public boolean isxincludeaware();
documentbuilder是一个抽象类,要获取documentbuilder实例,需要使用documentbuilderfactory。documentbuilderfactory提供了多种查找documentbuilder实现类的方法;documentbuilderfactory本身也是抽象类,它提供了两个静态方法来创建documentbuilderfactory实例:
public static documentbuilderfactory newinstance();
public static documentbuilderfactory newinstance(string factoryclassname, classloader classloader);
不带参数的newinstance()方法使用以下步骤查找documentbuilderfactory的实现类:
1. 查看系统属性中是否存在javax.xml.parsers.documentbuilderfactory为key的定义,如果存在,则使用该key定义的值作为documentbuilderfactory的实现类。
2. 查找${java.home}/lib/jaxp.properties属性文件中是否存在javax.xml.parsers.documentbuilderfactory为key的定义,若存在,则使用该属性文件中以该key定义的值作为documentbuilderfactory的实现类。
3. 查找当前classpath(包括jar包中)下是否存在meta-inf/services//javax.xml.parsers.documentbuilderfactory文件的定义(serviceprovider),若存在,则读取该文件中的第一行的值作为documentbuilderfactory的实现类。
4. 若以上都没有找到,则使用默认的documentbuilderfactory的实现类:
com.sun.org.apache.xerces.internal.jaxp.documentbuilderfactoryimpl
在找到相应的documentbuilderfactory实现类后,实例化该实现类,并返回documentbuilderfatory实例。这里的查找机制和xmlreaderfactory查找xmlreader实现类以及commons-logging查找logfactory的机制很像。
对带参数的newinstance()方法,直接使用参数中提供的documentbuilderfactory实现类以及classloader来创建documentbuilderfactory实例。
最后,在系统属性中将jaxp.debug设置为true可以打开调试信息。
在创建documentbuilderfactory实例后,如其名所示,它可以用于获取documentbuilder实例,另外,documentbuilderfactory还提供了配置解析器的方法:
public abstract documentbuilder newdocumentbuilder();
public void setnamespaceaware(boolean awareness);
public boolean isnamespaceaware();
public void setvalidating(boolean validating);
public boolean isvalidating();
public void setignoringelementcontentwhitespace(boolean whitespace);
public boolean isignoringelementcontentwhitespace();
public void setexpandentityreferences(boolean expandentityref);
public boolean isexpandentityreferences();
public void setignoringcomments(boolean ignorecomments);
public boolean isignoringcomments();
public void setcoalescing(boolean coalescing);
public boolean iscoalescing();
public void setxincludeaware(final boolean state);
public abstract void setattribute(string name, object value);
public abstract object getattribute(string name);
public abstract void setfeature(string name, boolean value);
public abstract boolean getfeature(string name);
public void setschema(schema schema);
在创建出documentbuilderfactory,使用该factory创建documentbuilder实例后,就可以使用该documentbuilder解析xml文件成一个document实例,而通过该document实例就可以遍历、查找dom树,从而获得想要的信息。在下面的例子中,遍历dom树,创建多个book实例:
public class w3cbooksdomreader {
private static documentbuilderfactory factory = documentbuilderfactory.newinstance();
private string booksxmlfile;
public w3cbooksdomreader(string booksxmlfile) {
this.booksxmlfile = booksxmlfile;
public list<book> parse() {
document doc = parsexmlfile();
element root = doc.getdocumentelement();
nodelist nodes = root.getelementsbytagname("book");
for(int i = 0; i < nodes.getlength(); i++) {
books.add(parsebookelement((element)nodes.item(i)));
}
private document parsexmlfile() {
file xmlfile = new file(booksxmlfile);
if(!xmlfile.exists()) {
throw new runtimeexception("cannot find xml file: " + booksxmlfile);
try {
documentbuilder builder = factory.newdocumentbuilder();
return builder.parse(xmlfile);
} catch(exception ex) {
throw new runtimeexception("failed to create documentbuilder instance", ex);
private book parsebookelement(element bookelement) {
string category = bookelement.getattribute("category");
string cover = bookelement.getattribute("cover");
nodelist nodes = bookelement.getelementsbytagname("title");
string lang = ((element)nodes.item(0)).getattribute("lang");
// first way to get content of an element
string title = ((text)((element)nodes.item(0)).getfirstchild()).getdata().trim();
list<string> authors = new arraylist<string>();
nodes = bookelement.getelementsbytagname("author");
// second way to get content of an element
string author = nodes.item(0).gettextcontent().trim();
authors.add(author);
nodes = bookelement.getelementsbytagname("year");
int year = integer.parseint(nodes.item(0).gettextcontent().trim());
nodes = bookelement.getelementsbytagname("price");
double price = double.parsedouble(nodes.item(0).gettextcontent().trim());
book.setcategory(category);
book.setcover(cover);
book.settitle(new titleinfo(title, lang));
book.setauthors(authors);
book.setyear(year);
book.setprice(price);
public string getbooksxmlfile() {
return booksxmlfile;
public static void main(string[] args) {
w3cbooksdomreader reader = new w3cbooksdomreader("resources/xmlfiles/w3c_books.xml");
list<book> books = reader.parse();
system.out.println("result:");
for(book book : books) {
system.out.println(book);
将对象实例序列化成xml文件,首先需要构建dom树,即要构建document实例,然后将该document实例写入的xml文件中。如上节所述,可以使用documentbuilder类来创建document实例,然后根据对象实例(book实例)和需要的xml格式构建节点和节点的排布即可,这里不再详述。
要将对象序列化成xml文件还要处理的另一个问题是如何将document实例写入到指定的xml文件中,在java中提供了transformer接口来做这件事情。这属于xlst(extensible stylesheet language)的范畴,不过这里不打算对其做详细介绍,主要关注如何将document实例输出成xml文件。
transformer提供了transform方法将document实例写入指定的流中:
public abstract void transform(source xmlsource, result outputtarget);
其中source接口定义了输入源,它可以是domsource,也可以是saxsource,或者是自定义的其他source子类,这里主要介绍domsource。source接口定义了systemid属性,它表示xml源的位置,xml源不是从url中获取的源来说,它为null。具体定义如下:
public interface source {
public void setsystemid(string systemid);
public string getsystemid();
domsource是对source的一个具体实现,它接收node、systemid信息:
public class domsource implements source {
private node node;
private string systemid;
public domsource() { }
public domsource(node n) {
setnode(n);
public domsource(node node, string systemid) {
setnode(node);
setsystemid(systemid);
result是对输出目的的抽象,即将输入源转换成目的源。同source接口,result接口也定义了systemid属性,表示目的文件位置,如果目的源不是url,则改值为null:
public interface result {
jdk中提供了多种result的实现,如domresult、streamresult等。这里只介绍streamresult,表示其输出目的是流,我们可以提供writer、outputstream等实例来接收这些输出:
public class streamresult implements result {
public streamresult() {
public streamresult(outputstream outputstream) {
setoutputstream(outputstream);
public streamresult(writer writer) {
setwriter(writer);
public streamresult(string systemid) {
this.systemid = systemid;
public streamresult(file f) {
setsystemid(f.touri().toasciistring());
private string systemid;
private outputstream outputstream;
private writer writer;
除了transform方法,transformer类还提供了其他的方法用于配置transformer在转换时用到的信息(只提供接口定义,不详述):
public abstract void setparameter(string name, object value);
public abstract object getparameter(string name);
public abstract void clearparameters();
public abstract void seturiresolver(uriresolver resolver);
public abstract uriresolver geturiresolver();
public abstract void setoutputproperties(properties oformat);
public abstract properties getoutputproperties();
public abstract void setoutputproperty(string name, string value);
public abstract string getoutputproperty(string name);
public abstract void seterrorlistener(errorlistener listener);
public abstract errorlistener geterrorlistener();
类似documentbuilder,transformer通过transformerfactory创建,而transformerfactory的创建如同documentbuilderfactory的创建以及查找机制,所不同的是transformerfactory的属性名为:javax.xml.transform.transformerfactory,其默认实现类为:com.sun.org.apache.xalan.internal.xsltc.trax.transformerfactoryimpl,而且它也提供了两个获取transformerfactory实例的方法,这里不再详述:
public static transformerfactory newinstance();
public static transformerfactory newinstance(string factoryclassname, classloader classloader);
transformerfactory提供了创建transformer和templates的方法,同时也提供了在创建这两个实例时可以设置的一些配置方法:
public abstract transformer newtransformer(source source);
public abstract transformer newtransformer();
public abstract templates newtemplates(source source);
public abstract source getassociatedstylesheet(source source, string media,
string title, string charset);
最后,提供一个完整的例子,使用本文开始时创建的list<book>实例序列化成xml文件:
public class w3cbooksdomwriter {
private static documentbuilder docbuilder;
private static transformer transformer;
static {
documentbuilderfactory factory = documentbuilderfactory.newinstance();
docbuilder = factory.newdocumentbuilder();
throw new runtimeexception("create documentbuilder instance failed.", ex);
transformerfactory transfactory = transformerfactory.newinstance();
transformer = transfactory.newtransformer();
throw new runtimeexception("create transformer instance failed.", ex);
transformer.setoutputproperty(outputkeys.encoding, "utf-8");
transformer.setoutputproperty(outputkeys.indent, "yes");
private list<book> books;
public w3cbooksdomwriter(list<book> books) {
this.books = books;
public void toxml(writer writer) throws exception {
document doc = builddomtree();
writetoxmlfile(writer, doc);
public document builddomtree() {
document doc = docbuilder.newdocument();
element root = doc.createelement("bookstore");
doc.appendchild(root);
for(book book : books) {
element bookelement = buildbookelement(doc, book);
root.appendchild(bookelement);
return doc;
public element buildbookelement(document doc, book book) {
element bookelement = doc.createelement("book");
bookelement.setattribute("category", book.getcategory());
bookelement.setattribute("cover", book.getcover());
titleinfo title = book.gettitle();
element titleelement = doc.createelement("title");
titleelement.setattribute("lang", title.getlang());
titleelement.settextcontent(title.gettitle());
bookelement.appendchild(titleelement);
for(string author : book.getauthors()) {
element authorelement = doc.createelement("author");
authorelement.settextcontent(author);
bookelement.appendchild(authorelement);
element yearelement = doc.createelement("year");
yearelement.settextcontent(string.valueof(book.getyear()));
bookelement.appendchild(yearelement);
element priceelement = doc.createelement("price");
priceelement.settextcontent(string.valueof(book.getprice()));
bookelement.appendchild(priceelement);
return bookelement;
public void writetoxmlfile(writer writer, document doc) throws exception {
domsource source = new domsource(doc);
streamresult result = new streamresult(writer);
transformer.transform(source, result);
public static void main(string[] args) throws exception {
stringwriter writer = new stringwriter();
list<book> books = w3cbooksbuilder.buildbooks();
w3cbooksdomwriter domwriter = new w3cbooksdomwriter(books);
domwriter.toxml(writer);
system.out.println(writer.tostring());