天天看点

XML解析-DOM解析

在做解析之前,我们先构建一个xml,以后相关的解析都针对这个xml进行操作。

<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
    <book id="1">
        <name>冰与火之歌</name>
        <author>乔治马丁</author>
        <year>2014</year>
        <price>89</price>
    </book>
    <book id="2">
        <name>安徒生童话</name>
        <year>2004</year>
        <price>77</price>
        <language>English</language>
    </book>    
</bookstore>
           

原理

DOM的全称是Document Object Model,也即文档对象模型。

xml解析器一次性把整个xml文档加载进内存,然后在内存中构建一颗Document的对象树,通过Document对象,得到树上的节点对象,通过节点对象访问(操作)到xml文档的内容。

Document对象代表了一个完整的xml文档,通过Document对象,可以得到其下面的其他节点对象,通过各个节点对象来访问xml文档的内容。

其中主要包括:标签节点,属性节点,文本节点和注释节点;并且各类节点也被封装成对应的对象,通过操作不同的对象来访问xml的内容。

优缺点

优点:

  1、形成了树结构,有助于更好的理解、掌握,且代码容易编写。

  2、解析过程中,树结构保存在内存中,方便修改。

缺点:

  1、由于文件是一次性读取,所以对内存的耗费比较大。

  2、如果XML文件比较大,容易影响解析性能且可能会造成内存溢出。

解析过程

  1. 建立一个解析器工厂
    //创建一个DocumentBuilderFactory的对象
    //DocumentBuilderFactory是一个抽象工厂类,它不能直接实例化,但该类提供了一个newInstance方法 ,这个方法会根据本地平台默认安装的解析器,自动创建一个工厂的对象并返回
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
               
    使用DocumentBuilderFacotry的目的是为了创建与具体解析器无关的程序,当 DocumentBuilderFactory类的静态方法newInstance()被调用时,它根据一个系统变量来决定具体使用哪一个解析器。又因为 所有的解析器都服从于JAXP所定义的接口,所以无论具体使用哪一个解析器,代码都是一样的。所以当在不同的解析器之间进行切换时,只需要更改系统变量的 值,而不用更改任何代码。这就是工厂所带来的好处。
  2. 获得一个DocumentBuilder对象
    //调用工厂对象的 newDocumentBuilder方法得到 DOM 解析器对象
    DocumentBuilder db = dbf.newDocumentBuilder();
               
    newDocumentBuilder()是一个抽象方法,通过它可以获得一个DocumentBuilder解析器对象,我们就可以利用这个解析器来对XML文档进行解析。
  3. 获取Document对象
    Document document = db.parse("books.xml");
                Document document = db.parse(new File("books.xml"));
               

    DocumentBuilder的parse()方法接受一个XML文档名作为输入参数,返回一个Document对象,这个Document对象就 代表了一个XML文档的树模型。以后所有的对XML文档的操作,都与解析器无关,直接在这个Document对象上进行操作就可以。

    这个方法进行了重载,可以接受不同的参数,返回Document对象。

    public Document parse(InputStream is)
            throws SAXException, IOException {
            if (is == null) {
                throw new IllegalArgumentException("InputStream cannot be null");
            }
            InputSource in = new InputSource(is);
            return parse(in);
        }
        
        public Document parse(InputStream is, String systemId)
            throws SAXException, IOException {
            if (is == null) {
                throw new IllegalArgumentException("InputStream cannot be null");
            }
            InputSource in = new InputSource(is);
            in.setSystemId(systemId);
            return parse(in);
        }
        
        public Document parse(String uri)
            throws SAXException, IOException {
            if (uri == null) {
                throw new IllegalArgumentException("URI cannot be null");
            }
            InputSource in = new InputSource(uri);
            return parse(in);
        }
    
        public Document parse(File f) throws SAXException, IOException {
            if (f == null) {
                throw new IllegalArgumentException("File cannot be null");
            }
            InputSource in = new InputSource(f.toURI().toASCIIString());
            return parse(in);
        }
    
        public abstract Document parse(InputSource is)
            throws SAXException, IOException;
               
  4. Dom操作

    因为xml以读取居多,很少涉及到dom更改和新增,所以这里不进行这部分操作了,有兴趣自己去百度就行。

    public static void main(String[] args) throws IOException, SAXException, ParserConfigurationException {
            //创建一个DocumentBuilderFactory的对象
            //DocumentBuilderFactory是一个抽象工厂类,它不能直接实例化,但该类提供了一个newInstance方法 ,这个方法会根据本地平台默认安装的解析器,自动创建一个工厂的对象并返回
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            //调用工厂对象的 newDocumentBuilder方法得到 DOM 解析器对象
            DocumentBuilder db = dbf.newDocumentBuilder();
            //通过DocumentBuilder对象的parser方法加载books.xml文件到当前项目下
            Document document = db.parse("C:\\Users\\Administrator\\Desktop\\books.xml");
            //去掉XML文档中作为格式化内容的空白而映射在DOM树中 的不必要的Text Node对象
        	document.normalize();
        	
            //获取根节点的元素对象
            Element root = document.getDocumentElement();
            listNodes(root);
    
            //获取所有book节点的集合
            NodeList nodeList = document.getElementsByTagName("book");
            for (int i = 0; i < nodeList.getLength(); i++) {
                //通过 item(i)方法 获取一个节点,nodelist的索引值从0开始
                Node node = nodeList.item(i);
                listNodes(node);
            }
    	}
         /**
         * 遍历指定节点
         * @param node
         */
         public static void listNodes(Node node) {
            // 节点是什么类型的节点
            if (node.getNodeType() == Node.ELEMENT_NODE) {// 判断是否是元素节点
                Element element = (Element) node;
    
                System.out.println("name:" + element.getNodeName() + "  value:"
                        + element.getNodeValue() + "  type:" + element.getNodeType() + " text:"+element.getTextContent());
                //判断此元素节点是否有属性
                if(element.hasAttributes()){
                    //获取属性节点的集合
                    NamedNodeMap namenm = 	element.getAttributes();//Node
                    //遍历属性节点的集合
                    for(int k=0;k<namenm.getLength();k++){
                        //获取具体的某个属性节点
                        Attr attr = (Attr) namenm.item(k);
                        System.out.println("attrName:"+attr.getNodeName()+" attrValue:"
                                +attr.getNodeValue()+" attrType:"+attr.getNodeType());
                    }
                }
                //获取元素节点的所有孩子节点
                NodeList listnode = element.getChildNodes();
                //遍历
                for (int j = 0; j < listnode.getLength(); j++) {
                    //得到某个具体的节点对象
                    Node nd = listnode.item(j);
                    //重新调用遍历节点的操作的方法
                    listNodes(nd);
                }
    
            }