Java之XML文件读取

作者: 秃头哥编程 | 来源:发表于2018-03-26 20:47 被阅读37次

    XML简介

    可扩展标记语言,简称XML(Extensible Markup Language),是一种标记语言。标记指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种信息的文章等。如何定义这些标记,既可以选择国际通用的标记语言,比如HTML,也可以使用像XML这样由相关人士自由决定的标记语言,这就是语言的可扩展性。XML是从标准通用标记语言(SGML)中简化修改出来的。它主要用到的有可扩展标记语言、可扩展样式语言(XSL)、XBRL和XPath等。
    —–维基百科

    XML是一种树结构,从根部开始,扩展到枝叶。
    book.xml
    <?xml version = "1.0" encoding = "UTF-8"?>
    <bookstore>
        <book id = "1">
            <name>Thinking in java</name>
            <author>Bruce Eckel</author>
            <year>2014</year>
        </book>
        <book id = "2" author = "liu">
            <name>爱的供养</name>
            <year>2004</year>
        </book>
    </bookstore>
    

    第一行是XML声明,它定义XML的版本和字符编码
    <bookstore>是根元素,它下面有三个子元素,</bookstore>是根元素的结尾也就是闭合标签,每个元素都必须要有相应的闭合标签,这不同于HTML。XML文档必须包含根元素,所有的元素都可以有子元素,只有你想。所有的元素可拥有文本内容和属性。同时,在XML中,标签是区分大小写的。


    Java对XML文件的读取

    Java对XML文件的操作一共有四种方式,分别是:

    DOM
    SAX
    JDOM
    DOM4J

    其中1和2是Java官方提供的对XML操作的方式。下面我们分别来谈谈这四种解析方式。

    DOM

    DOM是官方提供的与平台无关的解析方式,它解析XML文件的方式是一次性把XML文件全部读取进内存,这样做的缺点就是当文件非常大时,耗内存。

    优点:

    1.形成了树结构,直观好理解,代码容易编写。
    2.解析过程中树结构保存在内存中,方便修改。

    缺点:

    一次性把文件全部读取进内存,当文件大到一定程度时,耗费内存,且可能会造成内存溢出。

    下面来看看DOM读取XML文件的代码

    package com.codeliu.dom;
    import java.io.IOException;
    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
    import javax.xml.parsers.ParserConfigurationException;
    import org.w3c.dom.Document;
    import org.w3c.dom.NamedNodeMap;
    import org.w3c.dom.Node;
    import org.w3c.dom.NodeList;
    import org.xml.sax.SAXException;
    /**
     * @author liu
     * @version 创建时间:2018年3月24日 下午7:32:02
     * 使用DOM读取XML文件的内容 DOMTest.java
     */
    public class DOMTest {
        public static void main(String[] args) { 
            // 创建一个DocumentBuilderFactory对象 
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 
            try { 
                // 创建一个DocumentBuilder对象 
                DocumentBuilder db = dbf.newDocumentBuilder(); 
                // 使用parse方法解析xml文件 
                Document document = db.parse("book.xml"); 
                NodeList nl = document.getElementsByTagName("book"); 
                System.out.println("文件中包含有" + nl.getLength() + "本书"); 
                for(int i = 0; i < nl.getLength(); i++) { 
                    // 通过item(i)获取一个book节点 
                    Node book = nl.item(i); 
                    // 获取一个book节点的所有属性 
                    NamedNodeMap nnm = book.getAttributes(); 
                    for(int j = 0; j < nnm.getLength(); j++) { 
                        System.out.println(nnm.item(j).getNodeName() + ":" + nnm.item(j).getNodeValue());
                    } 
                    NodeList childNodes = book.getChildNodes(); 
                    // 子节点包括空格换行在内 
                    System.out.println("一个book节点下共有" + childNodes.getLength() + "个子节点"); 
                    for(int m = 0; m < childNodes.getLength(); m++) { 
                        // 如果子节点是element类型的,就输出 
                        if(childNodes.item(m).getNodeType() == Node.ELEMENT_NODE) { 
                            System.out.println(childNodes.item(m).getNodeName() +  ":" + childNodes.item(m).getFirstChild().getNodeValue()); 
                        } 
                    } 
                } 
            } 
            catch (ParserConfigurationException e) { 
                e.printStackTrace(); 
            } catch (SAXException e) { 
                e.printStackTrace(); 
            } catch (IOException e) { 
                e.printStackTrace(); 
            } 
        }
    }
    

    SAX

    SAX是官方提供的基于事件驱动的解析方式。所谓基于事件驱动的解析,就是在解析的过程中,是一步一步进行解析的,读到一个标签,就进行解析,不想DOM一样,一次性把整个文件全部读入内存。在后面的代码中我们会看到,使用SAX方式解析XML文件时,我们要写最近的Handler类继承DefaultHandler类,并重写startElement,endElement,startDocument,endDocument,以及characters方法,这样才能进行解析。

    优点:

    1.采用事件驱动,对内存耗费比较小。
    2.适用于只需要处理XML文件中的数据,而不关心XML结构的场景。

    缺点:

    1.不易编码,要继承DefaultHandler类并重写方法。
    2.很难同时访问同一个XML文件中不同地方的数据。

    下面来看看SAX解析XML文件的代码(因为要继承,所以写了3个类)

    import java.io.IOException;
    import javax.xml.parsers.ParserConfigurationException;
    import javax.xml.parsers.SAXParser;
    import javax.xml.parsers.SAXParserFactory;
    import org.xml.sax.SAXException;
    /**
     * @author liu
     * @version 创建时间:2018年3月24日 下午8:26:02
     * 使用SAX来解析XML文件 SAXTest.java
     */
    public class SAXTest { 
        public static void main(String[] args) { 
            // 创建一个SAXParserFactory对象 
            SAXParserFactory spf = SAXParserFactory.newInstance(); 
            try { 
                // 创建一个SAXParser对象 
                SAXParser parser = spf.newSAXParser(); 
                // 传入自定义的handler对象 
                MyHandler mh = new MyHandler(); 
                parser.parse("book.xml", mh); 
                for(Book book:mh.getBookList()) { 
                System.out.println(book); 
                } 
            } catch (ParserConfigurationException e) { 
                e.printStackTrace(); 
            } catch (SAXException e) { 
                e.printStackTrace(); 
            } catch (IOException e) { 
                e.printStackTrace(); 
            } 
        }
    }
    
    /**
     * @author liu
     * @version 创建时间:2018年3月24日 下午9:17:38
     * 读取每一本书的信息并进行保存 Book.java
     */
    public class Book { 
        private String id; 
        private String name; 
        private String author; 
        private String year; 
        /**
         * @return id
         */ 
        public String getId() { 
            return id; 
        } 
        /**
         * @param id 要设置的 id
         */
        public void setId(String id) { 
            this.id = id; 
        } 
        /**
         * @return name
         */ 
        public String getName() { 
            return name; 
        } 
        /**
         * @param name 要设置的 name 
         */ 
        public void setName(String name) { 
            this.name = name; 
        } 
        /**
         * @return author
         */ 
        public String getAuthor() { 
            return author; 
        } 
        /**
         * @param author 要设置的 author
         */ 
        public void setAuthor(String author) { 
            this.author = author; 
        } 
        /**
         * @return year
         */ 
        public String getYear() { 
            return year; 
        } 
        /**
         * @param year 要设置的 year
         */ 
        public void setYear(String year) { 
            this.year = year; 
        } 
        @Override 
        public String toString() { 
            return "Book [id=" + id + ", name=" + name + ", author=" + author + ", year=" + year + "]"; 
        }
    }
    
    package com.codeliu.SAX;
    import java.util.ArrayList;
    import javax.xml.stream.events.StartElement;
    import org.xml.sax.Attributes;
    import org.xml.sax.SAXException;
    import org.xml.sax.helpers.DefaultHandler;
    /**
     * @author liu
     * @version 创建时间:2018年3月24日 下午8:32:18
     * 继承DefaultHandler类 MyHandler.java
     */
    public class MyHandler extends DefaultHandler { 
        String value = " "; 
        Book book = null; 
        private ArrayList<Book> bookList = new ArrayList<Book>(); 
        /**
         * @return bookList
         */ 
        public ArrayList<Book> getBookList() { 
            return bookList; 
        } 
        /**
         * 用来标志解析开始
         */ 
        @Override 
        public void startDocument() throws SAXException { 
            super.startDocument(); 
            System.out.println("start"); 
        } 
        /**
         * 用来标志解析结束
         */
        @Override 
        public void endDocument() throws SAXException { 
            super.endDocument(); 
            System.out.println("end"); 
        } 
        /**
         * 用来遍历xml文件的开始标签 
         */ 
        @Override 
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 
            super.startElement(uri, localName, qName, attributes); 
            if(qName.equals("book")) { 
                book = new Book(); 
                System.out.println("遍历一本书开始"); 
                int num = attributes.getLength(); 
                for(int i = 0; i < num; i++) { 
                    if(attributes.getQName(i).equals("id")) { 
                        book.setId(attributes.getValue(i)); 
                    } else if(attributes.getQName(i).equals("author")) { 
                        book.setAuthor(attributes.getValue(i)); 
                    } 
                } 
            }  
        } 
        /**
         * 获取标签中的内容
         */ 
        @Override 
        public void characters(char[] ch, int start, int length) throws SAXException { 
            super.characters(ch, start, length); 
            value = new String(ch, start, length); 
        } 
        /**
         * 用来遍历xml文件的结束标签
         */ 
        @Override 
        public void endElement(String uri, String localName, String qName) throws SAXException { 
            super.endElement(uri, localName, qName); 
            if(qName.equals("book")) { 
                bookList.add(book); 
                book = null; 
                System.out.println("遍历一本书结束"); 
            } else if(qName.equals("name")) { 
                book.setName(value); 
            } else if(qName.equals("author")) { 
                book.setAuthor(value); 
            } else if(qName.equals("year")) { 
                book.setYear(value); 
            } 
        }
    }
    

    JDOM

    JDOM不是官方提供的解析方式,所以如果我们要使用的话,得先把jar包导入,下面是下载地址http://www.jdom.org/downloads/index.html

    如何在项目中导入jar包,相信大家都很熟悉,这里就不赘述,我建议大家把jar包导入的同时,把JDOM的源码一起导入,这样查看源码也方便。

    1.JDOM仅使用了具体类而没有使用接口
    2.JDOM的API大量使用了Collections类

    下面我们来看看JDOM解析XML的方式(Book.java在上面已经贴出来了)

    package com.codeliu.JDOM;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.ArrayList;
    import java.util.List;
    import org.jdom2.Attribute;
    import org.jdom2.Document;
    import org.jdom2.Element;
    import org.jdom2.JDOMException;
    import org.jdom2.input.SAXBuilder;
    /**
     * @author liu
     * @version 创建时间:2018年3月25日 上午8:34:47
     * 使用JDOM解析xml文件 JDOMTest.java
     */
    public class JDOMTest { 
        public static void main(String[] args) { 
            SAXBuilder sb = new SAXBuilder(); 
            // 遍历书,结果存储在books中 
            ArrayList<Book> books = new ArrayList<Book>(); 
            FileInputStream in; 
            try { 
                // 创建一个输入流,将xml文件加载到输入流中 
                in = new FileInputStream("book.xml"); 
                // 解析中文时出现乱码,可以改变编码格式 
                //InputStreamReader isr = new InputStreamReader(in, "utf-8"); 
                // 通过build方法,将输入流加载到SAXBuilder中 
                Document build = sb.build(in); 
                // 获取根节点 
                Element root = build.getRootElement(); 
                // 获取根节点下的子节点 
                List<Element> childList = root.getChildren(); 
                // 获取子节点的数量 
                System.out.println(childList.size()); 
                int count = 1; 
                for(Element child:childList) { 
                    Book book = new Book(); 
                    System.out.println("--------------开始解析第" + count + "本书----------------");
                    // 获取属性列表 
                    List<Attribute> attr = child.getAttributes(); 
                    for(Attribute a:attr) { 
                        // 输出子节点的属性名和属性值 
                        System.out.println(a.getName() + ":" + a.getValue()); 
                        if("id".equals(a.getName())) { 
                            book.setId(a.getValue()); 
                        } else if("author".equals(a.getName())) { 
                            book.setAuthor(a.getValue()); 
                        } 
                    } 
                    // 获取子节点下面的子节点 
                    List<Element> childrens = child.getChildren(); 
                    for(Element children:childrens) { 
                        // 输出子节点的子节点的属性名和属性值 
                        System.out.println(children.getName() + ":" + children.getText()); 
                        if("name".equals(children.getName())) { 
                            book.setName(children.getValue()); 
                        } else if("year".equals(children.getName())) { 
                            book.setYear(children.getValue()); 
                        } else if("author".equals(children.getName())) { 
                            book.setAuthor(children.getValue()); 
                        } 
                    } 
                    System.out.println("------------------第" + count + "本书解析完毕----------"); 
                    count++; 
                    books.add(book); 
                    book = null; 
                } 
                for(Book book:books) { 
                    System.out.println(book); 
                } 
            } catch (FileNotFoundException e) { 
                e.printStackTrace(); 
            } catch (JDOMException e) { 
                e.printStackTrace(); 
            } catch (IOException e) { 
                e.printStackTrace(); 
            } 
        }
    }
    

    DOM4J

    jar包下载地址https://github.com/dom4j/dom4j/releases/tag/dom4j_1_6_1

    1.DOM4J是JDOM的一种智能分支,它合并了许多超出基本xml文档表示的功能
    2.DOM4J使用接口和抽象基本类方法,是一个优秀的Java XML API
    3.具有性能优异,灵活性好,功能强大和易使用的特点
    4.源代码开放

    下面看看DOM4J解析XML文件的代码

    package com.codeliu.DOM4J;
    import java.util.Iterator;
    import java.util.List;
    import org.dom4j.Attribute;
    import org.dom4j.Document;
    import org.dom4j.DocumentException;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    /**
     * @author liu
     * @version 创建时间:2018年3月26日 上午11:22:29
     * 使用DOM4J解析xml文件
     */
    public class DOM4JTest { 
        public static void main(String[] args) { 
            SAXReader reader = new SAXReader(); 
            try { 
                Document read = reader.read("book.xml"); 
                // 获取根节点 
                Element root = read.getRootElement(); 
                // 通过elementIterator方法获取迭代器 
                Iterator books = root.elementIterator(); 
                // 遍历迭代器 
                while(books.hasNext()) { 
                    System.out.println("------------开始遍历一本书------------"); 
                    Element b = (Element)books.next(); 
                    // 获取每本书的属性 
                    List<Attribute> bookList = b.attributes(); 
                    for(Attribute a:bookList) { 
                        System.out.println(a.getName() + ":" + a.getValue()); 
                    } 
                    // 获取每本书下面的子节点 
                    Iterator childBook = b.elementIterator(); 
                    while(childBook.hasNext()) { 
                        Element c = (Element)childBook.next(); 
                        System.out.println(c.getName() + ":" + c.getStringValue()); 
                    } 
                    System.out.println("-------------遍历完成一本书------------"); 
                } 
            } catch (DocumentException e) { 
                e.printStackTrace(); 
            } 
        }
    }
    

    四种解析方式的对比

    我们可以把上面四种方式的解析代码整合到一个测试类里,使用System.currentTimeMillis()函数就可以对比解析同一个Book.xml各自的用时。我测试的结果如下:

    DOM:461

    SAX:21

    JDOM:264

    DOM4J:185

    本文用来测试的Book.xml文件非常小,有兴趣的读者可以试试用几兆或者几十兆的xml文件进行测试,结果又会不一样。现在DOM4J使用的比较广泛,其他三种也各有各的优势,具体用什么方式,要视情况而定。

    结束语

    写了快两个小时,差不多又写了近1w字了,学习Java任重道远呐,一起加油吧。

    相关文章

      网友评论

      本文标题:Java之XML文件读取

      本文链接:https://www.haomeiwen.com/subject/kdhdcftx.html