Loading

Java之XML文件读取

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的版本和字符编码
是根元素,它下面有三个子元素,是根元素的结尾也就是闭合标签,每个元素都必须要有相应的闭合标签,这不同于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个类)

package com.codeliu.SAX;
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(); 
        } 
    }
}
package com.codeliu.SAX;
/**
 * @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任重道远呐,一起加油吧。

posted @ 2018-03-26 20:14  CodeTiger  阅读(83)  评论(0编辑  收藏  举报