天天看点

DOM操作XML文件

使用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());