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的版本和字符编码
<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任重道远呐,一起加油吧。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容