开发者社区> 雪地脚印> 正文

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>

        <year>2005</year>

        <price>30.00</price>

    </book>

    <bookcategory="web"cover="paperback" >

        <title lang="en">Learning XML</title>

        <author>Erik T. Ray</author>

        <year>2003</year>

        <price>39.95</price>

    </book>

    <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>

        <year>2003</year>

        <price>49.99</price>

    </book>

</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解析XML文件

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类还提供了判断当前解析器是否存在命名空间解析、验证等配置,以及提供了设置EntityResolverErrorHandler的接口。这里使用EntityResolverErrorHandler只是重用SAXAPI,并不表示DOM解析的内部实现一定要基于SAX,然而貌似JDK自带的DOM解析内部使用的引擎就是SAXT_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实例,需要使用DocumentBuilderFactoryDocumentBuilderFactory提供了多种查找DocumentBuilder实现类的方法;DocumentBuilderFactory本身也是抽象类,它提供了两个静态方法来创建DocumentBuilderFactory实例:

public static DocumentBuilderFactory newInstance();

public static DocumentBuilderFactory newInstance(String factoryClassName, ClassLoader classLoader);

不带参数的newInstance()方法使用以下步骤查找DocumentBuilderFactory的实现类:

1.       查看系统属性中是否存在javax.xml.parsers.DocumentBuilderFactorykey的定义,如果存在,则使用该key定义的值作为DocumentBuilderFactory的实现类。

2.       查找${java.home}/lib/jaxp.properties属性文件中是否存在javax.xml.parsers.DocumentBuilderFactorykey的定义,若存在,则使用该属性文件中以该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 boolean isXIncludeAware();

 

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 Schema getSchema();

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");

       

        List<Book> books = new ArrayList<Book>();

        for(int i = 0; i < nodes.getLength(); i++) {

            books.add(parseBookElement((Element)nodes.item(i)));

        }

       

        return books;

    }

   

    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");

        for(int i = 0; i < nodes.getLength(); i++) {

            // 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 book = new Book();

        book.setCategory(category);

        book.setCover(cover);

        book.setTitle(new TitleInfo(title, lang));

        book.setAuthors(authors);

        book.setYear(year);

        book.setPrice(price);

        return book;

    }

   

    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);

        }

    }

}

DOM构建XML文件

将对象实例序列化成XML文件,首先需要构建DOM树,即要构建Document实例,然后将该Document实例写入的XML文件中。如上节所述,可以使用DocumentBuilder类来创建Document实例,然后根据对象实例(Book实例)和需要的XML格式构建节点和节点的排布即可,这里不再详述。

要将对象序列化成XML文件还要处理的另一个问题是如何将Document实例写入到指定的XML文件中,在Java中提供了Transformer接口来做这件事情。这属于XLSTEXtensible Stylesheet Language)的范畴,不过这里不打算对其做详细介绍,主要关注如何将Document实例输出成XML文件。

Transformer提供了transform方法将Document实例写入指定的流中:

public abstract void transform(Source xmlSource, Result outputTarget);

其中Source接口定义了输入源,它可以是DOMSource,也可以是SAXSource,或者是自定义的其他Source子类,这里主要介绍DOMSourceSource接口定义了systemId属性,它表示XML源的位置,XML源不是从URL中获取的源来说,它为null。具体定义如下:

public interface Source {

    public void setSystemId(String systemId);

    public String getSystemId();

}

DOMSource是对Source的一个具体实现,它接收NodesystemId信息:

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 {

    public void setSystemId(String systemId);

    public String getSystemId();

}

JDK中提供了多种Result的实现,如DOMResultStreamResult等。这里只介绍StreamResult,表示其输出目的是流,我们可以提供WriterOutputStream等实例来接收这些输出:

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 void reset();

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

类似DocumentBuilderTransformer通过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提供了创建TransformerTemplates的方法,同时也提供了在创建这两个实例时可以设置的一些配置方法:

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);

public abstract void setURIResolver(URIResolver resolver);

public abstract URIResolver getURIResolver();

public abstract void setFeature(String name, boolean value);

public abstract boolean getFeature(String name);

public abstract void setAttribute(String name, Object value);

public abstract Object getAttribute(String name);

public abstract void setErrorListener(ErrorListener listener);

public abstract ErrorListener getErrorListener();

最后,提供一个完整的例子,使用本文开始时创建的List<Book>实例序列化成XML文件:

public class W3CBooksDOMWriter {

    private static DocumentBuilder docBuilder;

    private static Transformer transformer;

   

    static {

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        try {

            docBuilder = factory.newDocumentBuilder();

        } catch(Exception ex) {

            throw new RuntimeException("Create DocumentBuilder instance failed.", ex);

        }

       

        TransformerFactory transFactory = TransformerFactory.newInstance();

        try {

            transformer = transFactory.newTransformer();

        } catch(Exception ex) {

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

    }

}


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Javascript操作DOM常用API总结
文本整理了javascript操作DOM的一些常用的api,根据其作用整理成为创建,修改,查询等多种类型的api,主要用于复习基础知识,加深对原生js的认识。
884 0
Java对文件的读、写随机访问,RandomAccessFile类的使用分析
  在网上看了一些关于java中的RandomAccessFile类的介绍,又经过查看Java API和自己编的测试程序,总算是对RandomAccessFile的使用有了一定的了解。自己做了以下比较详细的总结吧。
1080 0
java操作XML中的占位符
有时XML的某些内容是待定的,对于这些内容可以在XML文件中使用占位符 &lt;people&gt; &lt;person id="001"&gt; &lt;name&gt;$name&lt;/name&gt; &lt;age&gt;$age&lt;/age&gt; &lt;/person&gt; &lt;/people&gt;   读取该文件的时候可以为其中的
840 0
Qt-QML-C++交互实现文件IO系统-后继-读取XML文件和创建XML文件
在前面两篇中,大致完成了一个文件IO的读和写操作。前面两篇文章链接
75 0
xml文件转二进制文件的AIR
游戏开发中,有很多XML可能是很大的,比如一个任务配置文件,可能就接近2M(当然全部文件放在一个XML里面本身就有问题,比较好的做法就是分等级分隔XML),这样就需要对XML进行压缩。之前把文件压成ZIP包,然后读取ZIP,但现在读取ZIP文件里面的内容,是很卡的,后面改成读取二进制的XML数据,这样感觉不会卡,速度也很快。
627 0
boost操作xml 5分钟官方教程
Five Minute Tutorial This tutorial uses XML. Note that the library is not specifically bound to XML, and any other supported format (such as INI or JSON) could be used instead.
833 0
java中四种操作xml方式的比较
java中四种操作xml方式的比较1. 介绍 1)DOM(JAXP Crimson解析器) DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准。DOM是以层次结构组织的节点或信息片断的集合。
625 0
Windows改桌面文件路径
默认的桌面和用户文件都是C盘,每次重装系统要备份,为了方便可以把它设置到其他盘符,一种方式是通过一些软件功能,如360有一个C盘搬家,也可以修改注册表文件: Windows Registry Editor Version 5.
519 0
kbmmw 中XML 操作入门
delphi 很早以前就自带了xml 的操作,最新版里面有三种XML 解释器,一种是MSXML,看名字就知道 这个是微软自带的,这个据delphi 官方称是速度是最快的,但是只能在windows 上使用。
856 0
+关注
116
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载