DOM,SAX,JDOM,DOM4J 四种方式解析xml

时间:2023-02-26 20:05:30

首先创建一个xml文档,文档内容如下


<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book id="1">
<name>编程之美</name>
<price>34.0</price>
<author>《编程之美》小组</author>
</book>
<book id="2">
<name>平凡的世界</name>
<price>56.0</price>
<author>路遥</author>
<language>中文</language>
</book>
</bookstore>


因为xml定义是书店的书本详情,解析完xml之后,希望把解析之后的数据保存下来,所以定义了一个Book的Entity,定义好各个属性


package com.ikok.parsexml;

public class Book {
private String id;
private String name;
private String price;
private String author;
private String language;

public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}

}


DOM解析:


package com.ikok.parsexml;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

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;

public class domXml {

private static List<Book> booksList = new ArrayList<Book>();

public static void main(String[] args) {
// 创建一个DocumentBuilderFactory对象
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
// 创建一个DocumentBuilder对象
DocumentBuilder db = dbf.newDocumentBuilder();
// 通过DocumentBuilder对象的parse方法加载xml文件到当前项目
Document document = db.parse("books.xml");
// 获取所有book节点的集合
NodeList bookList = document.getElementsByTagName("book");
// 遍历每一个book节点,通过bookList.getLength()方法知道集合的长度
System.out.println("一共有 " + bookList.getLength() + " 本书");
for (int i = 0; i < bookList.getLength(); i++) {
Book bookItem = new Book();
System.out.println("-----------开始遍历第" + (i+1) + "本书的内容-----------");
// 通过item(i)的方法获取一个book节点,NodeList的索引值从0开始
Node book = bookList.item(i);
// 获取book节点的所有属性集合
NamedNodeMap attrs = book.getAttributes();
System.out.println("第 " + (i+1) + " 本书共有 " + attrs.getLength() + " 个属性");
// 遍历book的属性
for (int j = 0; j < attrs.getLength(); j++) {
// 通过item(i)的方法获取一个book节点的某个属性
Node attr = attrs.item(j);
// 获取属性名
System.out.print("属性名为:" + attr.getNodeName());
// 获取属性值
System.out.println("----属性值为:" + attr.getNodeValue());
if(attr.getNodeName().equals("id")){
bookItem.setId(attr.getNodeValue());
}
}

// 当知道节点有且只有一个属性时,进行强制类型转换,通过getAttribute()方法获取属性值
//Element book = (Element) bookList.item(i);
//String attrValue = book.getAttribute("id");
//System.out.println("id :" + attrValue);

//解析book节点的子节点
NodeList childList = book.getChildNodes();
System.out.println("第 " + (i+1) + " 本书共有 " + childList.getLength() + " 个子节点");
for (int k = 0; k < childList.getLength(); k++) {
// 区分出text类型的node以及element类型node
if (childList.item(k).getNodeType() == Node.ELEMENT_NODE) {
// 获取了element类型节点的节点名
System.out.print("第 " + (i+1) + " 本书的子节点名: " + childList.item(k).getNodeName() + "----");
// 如果当前节点中还有节点,则属性值为空。比如<name><a>aa</a><b>bb</b>第一行代码</name>
System.out.println(" 属性值: " + childList.item(k).getFirstChild().getNodeValue());
// 这样会输出所有子节点和节点之间的内容。会输出 aabb第一行代码
//System.out.println(childList.item(k).getTextContent());
if(childList.item(k).getNodeName().equals("name")){
bookItem.setName(childList.item(k).getFirstChild().getNodeValue());
} else if(childList.item(k).getNodeName().equals("price")){
bookItem.setPrice(childList.item(k).getFirstChild().getNodeValue());
} else if(childList.item(k).getNodeName().equals("author")){
bookItem.setAuthor(childList.item(k).getFirstChild().getNodeValue());
} else if(childList.item(k).getNodeName().equals("language")){
bookItem.setLanguage(childList.item(k).getFirstChild().getNodeValue());
}
}
}

System.out.println("-----------结束遍历第" + (i+1) + "本书的内容-----------");
System.out.println();
booksList.add(bookItem);
bookItem = null;
}
for (Book item : booksList) {
System.out.println("书本id :" + item.getId());
System.out.println("书本名字 :" + item.getName());
System.out.println("书本价格 :" + item.getPrice());
System.out.println("书本作者 :" + item.getAuthor());
System.out.println("书本语言 :" + item.getLanguage());
System.out.println("---------------------------------");
}
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}
结果:

一共有 2 本书
-----------开始遍历第1本书的内容-----------
第 1 本书共有 1 个属性
属性名为:id----属性值为:1
第 1 本书共有 7 个子节点
第 1 本书的子节点名: name---- 属性值: 编程之美
第 1 本书的子节点名: price---- 属性值: 34.0
第 1 本书的子节点名: author---- 属性值: 《编程之美》小组
-----------结束遍历第1本书的内容-----------

-----------开始遍历第2本书的内容-----------
第 2 本书共有 1 个属性
属性名为:id----属性值为:2
第 2 本书共有 9 个子节点
第 2 本书的子节点名: name---- 属性值: 平凡的世界
第 2 本书的子节点名: price---- 属性值: 56.0
第 2 本书的子节点名: author---- 属性值: 路遥
第 2 本书的子节点名: language---- 属性值: 中文
-----------结束遍历第2本书的内容-----------

书本id :1
书本名字 :编程之美
书本价格 :34.0
书本作者 :《编程之美》小组
书本语言 :null
---------------------------------
书本id :2
书本名字 :平凡的世界
书本价格 :56.0
书本作者 :路遥
书本语言 :中文
---------------------------------



SAX解析:


package com.ikok.parsexml;

import java.io.IOException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.SAXException;

public class SAXXml {

public static void main(String[] args) {
// 创建一个SAXParserFactory的实例
SAXParserFactory saxpf = SAXParserFactory.newInstance();
try {
// 通过SAXParserFactory的实例获取SAXParser的实例
SAXParser parser = saxpf.newSAXParser();
SAXParserHandler handler = new SAXParserHandler();
parser.parse("books.xml", handler);
System.out.println("共有 " + handler.getBookList().size() + " 本书");
for (Book book : handler.getBookList()) {
System.out.println("书本id :" + book.getId());
System.out.println("书本名字 :" + book.getName());
System.out.println("书本价格 :" + book.getPrice());
System.out.println("书本作者 :" + book.getAuthor());
System.out.println("书本语言 :" + book.getLanguage());
System.out.println("---------------------------------");
}
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}


因为SAX解析xml的解析方法需要一个DefaultHandler参数,所以我定义了一个SAXParserHandler来继承这个类,重写部分方法


package com.ikok.parsexml;

import java.util.ArrayList;
import java.util.List;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class SAXParserHandler extends DefaultHandler {

// 书本的序号
int bookIndex = 0;
/**
* 某个属性值
* 因为属性名在startElement()方法中得到,而属性值却在characters()方法中得到
* 为了能把属性名和属性值加到book实例中去,在endElement()方法前,另外两个方法都执行了,所以在这进行处理
*/
String value = null;
Book book = null;
private List<Book> bookList = new ArrayList<Book>();

public List<Book> getBookList() {
return bookList;
}


// 用来遍历xml文件的开始标签
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
// 开始解析book元素的属性
if(qName.equals("book")){
// 创建一个book对象
book = new Book();
bookIndex++;
System.out.println("-----------开始遍历第" + bookIndex + "本书的内容-----------");
//// 已知book元素下属性的名称,根据属性名获取属性值
//String value = attributes.getValue("id");
//System.out.println("id:" + value);
// 不知道book元素下属性的名称以及个数,获取属性名及属性值
// 属性个数
int num = attributes.getLength();
for (int i = 0; i < num; i++) {
System.out.print("第 " + (i+1) + "个属性名: " + attributes.getQName(i));
System.out.println(" 属性值: " + attributes.getValue(i));
if (attributes.getQName(i).equals("id")) {
book.setId(attributes.getValue(i));
}
}
} else if(!qName.equals("book") && !qName.equals("bookstore")){
System.out.print("节点名:" + qName + " 节点值:");
}
};

// 用来遍历xml文件的结束标签
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
// 针对一本书的内容是否遍历结束
if (qName.equals("book")) {
// 清空book,方便执行下一个
bookList.add(book);
book = null;
System.out.println("-----------结束遍历第" + bookIndex + "本书的内容-----------");
} else if(qName.equals("name")){
book.setName(value);
} else if(qName.equals("price")){
book.setPrice(value);
} else if(qName.equals("author")){
book.setAuthor(value);
} else if(qName.equals("language")){
book.setLanguage(value);
}

};

// 用来标志解析开始
@Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub
super.startDocument();
System.out.println("SAX解析开始");
}

// 用来标志解析结束
@Override
public void endDocument() throws SAXException {
// TODO Auto-generated method stub
super.endDocument();
System.out.println("SAX解析结束");
}

@Override
public void characters(char[] ch, int start, int length) throws SAXException {
// TODO Auto-generated method stub
super.characters(ch, start, length);
// 获取了属性值
value = new String(ch, start, length);
// 默认情况下,会把xml中的换行符也算进去,输出格式不正确,去掉换行符
if (!value.trim().equals("")) {
System.out.println(value);
}
}




}
结果:

SAX解析开始
-----------开始遍历第1本书的内容-----------
第 1个属性名: id 属性值: 1
节点名:name 节点值:编程之美
节点名:price 节点值:34.0
节点名:author 节点值:《编程之美》小组
-----------结束遍历第1本书的内容-----------
-----------开始遍历第2本书的内容-----------
第 1个属性名: id 属性值: 2
节点名:name 节点值:平凡的世界
节点名:price 节点值:56.0
节点名:author 节点值:路遥
节点名:language 节点值:中文
-----------结束遍历第2本书的内容-----------
SAX解析结束
共有 2 本书
书本id :1
书本名字 :编程之美
书本价格 :34.0
书本作者 :《编程之美》小组
书本语言 :null
---------------------------------
书本id :2
书本名字 :平凡的世界
书本价格 :56.0
书本作者 :路遥
书本语言 :中文
---------------------------------


JDOM解析:


package com.ikok.parsexml;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
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;

public class JdomXml {

private static List<Book> bookList = new ArrayList<Book>();

public static void main(String[] args) {
// 创建一个SAXBuilder对象
SAXBuilder saxbuilder = new SAXBuilder();
// 创建一个输入流,将xml加载到输入流中
InputStream is;
try {
is = new FileInputStream("books.xml");
// 防止解析乱码,将xml的encoding属性改为UTF-8,或者在代码中设置输入流的编码方式
//InputStreamReader isr = new InputStreamReader(is, "UTF-8");
//Document document = (Document) saxbuilder.build(isr);
// 通过saxbuilder.build()方法,将输入流加载到saxbuilder中
Document document = (Document) saxbuilder.build(is);
// 通过document对象获取xml的根节点
Element rootElement = document.getRootElement();
// 获取根节点下的子节点的集合
List<Element> elementList = rootElement.getChildren();
for (Element element : elementList) {
Book book = new Book();
System.out.println("---------开始解析第" + (elementList.indexOf(element)+1) + "本书-----------------");
//// 知道属性的名字及个数
//System.out.println(element.getAttributeValue("id"));
// 获取属性集合,不知道属性的名字及个数
List<Attribute> attrList = element.getAttributes();
for (Attribute attribute : attrList) {
// 属性名
String attrName = attribute.getName();
// 属性值
String attrValue = attribute.getValue();
// 存储到Book中
if (attrName.equals("id")) {
book.setId(attrValue);
}
System.out.println("属性名:" + attrName + "----属性值:" + attrValue);

}
// 对book节点下的子节点的节点名以及节点值进行解析
List<Element> childList = element.getChildren();
for (Element child : childList) {
// 属性名
String attrName = child.getName();
// 属性值
String attrValue = child.getValue();
// 存储到Book中
if(attrName.equals("name")){
book.setName(attrValue);
} else if(attrName.equals("price")){
book.setPrice(attrValue);
} else if(attrName.equals("author")){
book.setAuthor(attrValue);
} else if(attrName.equals("language")){
book.setLanguage(attrValue);
}
System.out.println("子节点名:" + attrName + "----子节点属性值:" + attrValue);
}
System.out.println("---------结束解析第" + (elementList.indexOf(element)+1) + "本书-----------------");
bookList.add(book);
book = null;
}
for (Book item : bookList) {
System.out.println("书本id :" + item.getId());
System.out.println("书本名字 :" + item.getName());
System.out.println("书本价格 :" + item.getPrice());
System.out.println("书本作者 :" + item.getAuthor());
System.out.println("书本语言 :" + item.getLanguage());
System.out.println("---------------------------------");
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JDOMException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

结果:

---------开始解析第1本书-----------------
属性名:id----属性值:1
子节点名:name----子节点属性值:编程之美
子节点名:price----子节点属性值:34.0
子节点名:author----子节点属性值:《编程之美》小组
---------结束解析第1本书-----------------
---------开始解析第2本书-----------------
属性名:id----属性值:2
子节点名:name----子节点属性值:平凡的世界
子节点名:price----子节点属性值:56.0
子节点名:author----子节点属性值:路遥
子节点名:language----子节点属性值:中文
---------结束解析第2本书-----------------
书本id :1
书本名字 :编程之美
书本价格 :34.0
书本作者 :《编程之美》小组
书本语言 :null
---------------------------------
书本id :2
书本名字 :平凡的世界
书本价格 :56.0
书本作者 :路遥
书本语言 :中文
---------------------------------


DOM4J解析:


package com.ikok.parsexml;

import java.io.File;
import java.util.ArrayList;
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;

public class dom4jXml {

private static List<Book> bookList = new ArrayList<Book>();
// 书的序号
static int bookIndex = 0;

public static void main(String[] args) {
// 创建SAXReader的对象
SAXReader reader = new SAXReader();

try {
// 通过reader的read()方法加载xml文件,获取document对象
Document document = reader.read(new File("books.xml"));
// 获取根节点
Element rootElement = document.getRootElement();
// 获取迭代器
Iterator iterator = rootElement.elementIterator();
// 遍历迭代器,获取根节点中的信息
while (iterator.hasNext()) {
bookIndex++;
Book bookItem = new Book();
System.out.println("----------开始遍历第" + bookIndex + "本书------------");
Element book = (Element) iterator.next();
// 获取属性名和属性值
List<Attribute> attrList = book.attributes();
for (Attribute attr : attrList) {
if(attr.getName().equals("id")){
bookItem.setId(attr.getValue());
}
System.out.println("属性名:" + attr.getName() + "----属性值:" + attr.getValue());
}
Iterator it = book.elementIterator();
while (it.hasNext()) {
Element element = (Element) it.next();
if(element.getName().equals("name")){
bookItem.setName(element.getStringValue());
} else if(element.getName().equals("price")){
bookItem.setPrice(element.getStringValue());
} else if(element.getName().equals("author")){
bookItem.setAuthor(element.getStringValue());
} else if(element.getName().equals("language")){
bookItem.setLanguage(element.getStringValue());
}
System.out.println("子节点名:" + element.getName() + "----子节点属性值:" + element.getStringValue());
}
System.out.println("----------结束遍历第" + bookIndex + "本书------------");
bookList.add(bookItem);
bookItem = null;
}
for (Book item : bookList) {
System.out.println("书本id :" + item.getId());
System.out.println("书本名字 :" + item.getName());
System.out.println("书本价格 :" + item.getPrice());
System.out.println("书本作者 :" + item.getAuthor());
System.out.println("书本语言 :" + item.getLanguage());
System.out.println("---------------------------------");
}
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
结果:

----------开始遍历第1本书------------
属性名:id----属性值:1
子节点名:name----子节点属性值:编程之美
子节点名:price----子节点属性值:34.0
子节点名:author----子节点属性值:《编程之美》小组
----------结束遍历第1本书------------
----------开始遍历第2本书------------
属性名:id----属性值:2
子节点名:name----子节点属性值:平凡的世界
子节点名:price----子节点属性值:56.0
子节点名:author----子节点属性值:路遥
子节点名:language----子节点属性值:中文
----------结束遍历第2本书------------
书本id :1
书本名字 :编程之美
书本价格 :34.0
书本作者 :《编程之美》小组
书本语言 :null
---------------------------------
书本id :2
书本名字 :平凡的世界
书本价格 :56.0
书本作者 :路遥
书本语言 :中文
---------------------------------


这里,JDOM我用的是2.0.5的包,DOM4J我用的是1.6.1的包。

以当前这个XML为解析的目标的情况下,解析耗时,SAX耗时比DOM短,DOM4J耗时比JDOM短。

目前hibernate框架中也是使用的DOM4J解析XML。