文章目录
XML解析模型
解析XML文件一般有两种模型,一种是DOM(文档)模型,另一种是流模型。
DOM模型
DOM的全称是Document Object Model,也即文档对象模型,它是与平台无关的。它是将XML文件转换为一个对象文档模型(即DOM树),然后程序可以对该对象文档模型进行操作,进而操作XML中的数据。
DOM解析优缺点
优点:
- 形成树结构,有助于更好的理解、掌握,并且编写代码比较容易
- 解析过程中,文档结构保存在内存中,方便读取,方便修改
不足:
- 一次性读取到内存中,所以内存消耗很大
- 对于大文件解析可能有性能损失,可能会造成内存溢出
流模式
流模式解析文档的时候与DOM模式有很大不同,它不会一次性将所有内容装入内存,而是从头部开始一段一段处理。
优点:
- 对内存耗费比较小
- 适用于只处理XML文件中的数据时
不足:
- 很难同时访问XML文件中的多处不同数据
- 不能编辑
- 只能前进不能后退
事件类型
在 javax.xml.stream.XMLStreamConstants中可以看到;下表中也有部分表示
名称 | 值 | 意义 |
---|---|---|
START_ELEMENT | 1 | 元素开始 |
END_ELEMENT | 2 | 元素结束 |
PROCESSING_INSTRUCTION | 3 | 处理指令 |
CHARACTERS | 4 | 字符(文本或空格) |
COMMENT | 5 | 注释 |
SPACE | 6 | 可忽略空格 |
START_DOCUMENT | 7 | 文档开始 |
END_DOCUMENT | 8 | 文档结束 |
ENTITY_REFERENCE | 9 | 实体引用 |
ATTRIBUTE | 10 | 元素属性 |
DTD | 11 | DTD |
CDATA | 12 | CDATA |
NAMESPACE | 13 | 命名空间 |
NOTATION_DECLARATION | 14 | 标记声明 |
ENTITY_DECLARATION | 15 | 实体声明 |
在流模式中有两种处理,一种是推模式(SAX),一种是拉模式(STAX),简单介绍一下这两种模式。
SAX
SAX方式不是标准的W3C标准,但是它是一个广泛使用的API。SAX的解析器不是在内存中建立DOM数结构,而是在读取文件时触发一系列的事件。这些事件推给事件处理器,由事件处理器处理。
- DTDHandler接口 用于访问XML DTD内容的
- ErrorHandler接口 用于低级访问解析错误的
- ContentHandler接口 用于访问文档内容的最普遍类型
当然为了使用方便SAX提供了一个DefaultHandler类,方便使用;它实现了DTDHandler, ContentHandler, ErrorHandler这三个接口
STAX
与SAX模式不同,STAX模式在处理文档时,用户可以定制自己感兴趣的节点主动从读取器中进行拉取,选择性的处理节点。大大增加了灵活性。
SAX与STAX对比
我们将使用这两种方式实现同样的功能,从是测试代码。
从这些代码中我们能看到,使用STAX方式解析XML比使用SAX方便很多,SAX需要你预先先定义事件处理器,才能正常解析。
SaxTest
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class SaxTest {
public static void main(String[] args) {
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
SAXParser parser = factory.newSAXParser();
BookHandler handler = new BookHandler();
parser.parse("src/main/resources/books.xml", handler);
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class BookHandler extends DefaultHandler {
private String value;
/**
* 解析xml元素
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
// 调用DefaultHandler类的startElement方法
super.startElement(uri, localName, qName, attributes);
if (qName.equals("name")) {
value = null;
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
// 调用DefaultHandler类的endElement方法
super.endElement(uri, localName, qName);
// 判断是否针对一本书已经遍历结束
if (qName.equals("name")) {
System.out.println(value);
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
value = new String(ch, start, length);
}
}
StaxTest
import java.io.FileInputStream;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.events.XMLEvent;
public class StaxTest {
public static void main(String[] args) throws Exception {
XMLStreamReader reader = XMLInputFactory.newInstance()
.createXMLStreamReader(new FileInputStream("src/main/resources/books.xml"));
while (reader.hasNext()) {
int eventType = reader.next();
if (eventType == XMLEvent.START_ELEMENT && reader.getLocalName().equals("name")) {
reader.next();
System.out.println(reader.getText());
}
}
}
}
XML解析技术
常用的XML解析技术有DOM、SAX,这两种方式都是偏于底层,对于Java来讲,还有两种方式,是对底层的封装,是高级API:JDOM和DOM4J。
接下来我们将用源码的形式,来展现如何通过这四种方式读取同一段XML文件。
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book id="1">
<name>Java编程思想</name>
<author>Bruce Eckel</author>
<price>108</price>
<press>机械工业出版社</press>
</book>
<book id="2">
<name>深入理解Java虚拟机</name>
<author>周志明</author>
<price>79</price>
<press>机械工业出版社</press>
</book>
</books>
使用的同一个实体
public class Book {
private String id;
private String name;
private String author;
private String price;
private String press;
//忽略getter和setter
}
使用DOM方式解析XML示例
import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class DomRead {
public static void main(String arge[]) {
File f = new File("src/main/resources/books.xml");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(f);
NodeList books = doc.getElementsByTagName("book");
for (int i = 0; i < books.getLength(); i++) {
Node book = books.item(i);
System.out.printf("bookId : %s %n",
book.getAttributes().getNamedItem("id").getNodeValue());
NodeList childNodes = book.getChildNodes();
Node child = null;
for (int j = 0; j < childNodes.getLength(); j++) {
child = childNodes.item(j);
if (child.getNodeType() == 1) {
System.out.printf("\t%s: %s %n", child.getNodeName(),
child.getFirstChild().getNodeValue());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用SAX解析XML示例
对于每一种XML文件格式,都要定义一个处理器来处理它:
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 BookSAXParserHandler extends DefaultHandler {
String value = null;
Book book = null;
private ArrayList<Book> bookList = new ArrayList<>();
public List<Book> getBookList() {
return bookList;
}
/**
* 解析xml元素
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
//调用DefaultHandler类的startElement方法
super.startElement(uri, localName, qName, attributes);
if (qName.equals("book")) {
book = new Book();
int num = attributes.getLength();
for (int i = 0; i < num; i++) {
if (attributes.getQName(i).equals("id")) {
book.setId(attributes.getValue(i));
}
}
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
//调用DefaultHandler类的endElement方法
super.endElement(uri, localName, qName);
//判断是否针对一本书已经遍历结束
if (qName.equals("book")) {
bookList.add(book);
book = null;
} else if (qName.equals("name")) {
book.setName(value);
} else if (qName.equals("author")) {
book.setAuthor(value);
} else if (qName.equals("price")) {
book.setPrice(value);
} else if (qName.equals("press")) {
book.setPress(value);
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
value = new String(ch, start, length);
}
}
解析测试
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 SAXRead {
public static void main(String[] args) {
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
SAXParser parser = factory.newSAXParser();
BookSAXParserHandler handler = new BookSAXParserHandler();
parser.parse("src/main/resources/books.xml", handler);
for (Book book : handler.getBookList()) {
System.out.printf("bookId : %s %n", book.getId());
System.out.printf("\tname: %s %n", book.getName());
System.out.printf("\tauthor: %s %n", book.getAuthor());
System.out.printf("\tprice: %s %n", book.getPrice());
System.out.printf("\tpress: %s %n", book.getPress());
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
JDOM
JDOM是直接为Java编程服务的。它利用更为强有力的JAVA语言的诸多特性(方法重载、集合概念以及映射),把SAX和DOM的功能有效地结合起来。百度百科
JDOM解析优缺点
特点:
- 仅使用具体类而不使用接口。这在某些方面简化了 API,但是也限制了灵活性。
- API 大量使用了 Collections 类,简化了那些已经熟悉这些类的 Java 开发者的使用。
JDOM解析XML示例
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
/**
* @author zhangshimin
*
*/
public class JDOMReader {
/**
* @param args
*/
public static void main(String[] args) {
String xmlPath = "src/main/resources/books.xml";
List<Book> books = getBooksFromXML(xmlPath);
for (Book book : books) {
System.out.printf("bookId : %s %n", book.getId());
System.out.printf("\tname: %s %n", book.getName());
System.out.printf("\tauthor: %s %n", book.getAuthor());
System.out.printf("\tprice: %s %n", book.getPrice());
System.out.printf("\tpress: %s %n", book.getPress());
}
}
private static List<Book> getBooksFromXML(String xmlPath) {
List<Book> books = new ArrayList<>();
SAXBuilder saxBuilder = new SAXBuilder();
InputStream in;
try {
// 2.创建一个输入流,将xml文件加载到输入流中
in = new FileInputStream(xmlPath);
InputStreamReader isr = new InputStreamReader(in, "UTF-8");
// 3.通过saxBuilder的build方法,将输入流加载到saxBuilder中
Document document = saxBuilder.build(isr);
// 4.通过document对象获取xml文件的根节点
Element rootElement = document.getRootElement();
// 5.获取根节点下的子节点的List集合
List<Element> bookList = rootElement.getChildren();
// 继续进行解析
Book bookEntity = null;
for (Element book : bookList) {
bookEntity = new Book();
// 解析book的属性集合
List<Attribute> attrList = book.getAttributes();
for (Attribute attr : attrList) {
if (attr.getName().equals("id")) {
bookEntity.setId(attr.getValue());
}
}
// 对book节点的子节点的节点名以及节点值的遍历
List<Element> bookChilds = book.getChildren();
for (Element child : bookChilds) {
if (child.getName().equals("name")) {
bookEntity.setName(child.getValue());
} else if (child.getName().equals("author")) {
bookEntity.setAuthor(child.getValue());
} else if (child.getName().equals("price")) {
bookEntity.setPrice(child.getValue());
} else if (child.getName().equals("press")) {
bookEntity.setPress(child.getValue());
}
}
books.add(bookEntity);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return books;
}
}
DOM4J
DOM4J是一个Java的XML API,是JDOM的升级品。DOM4J是一个十分优秀的JavaXML API,具有性能优异、功能强大和极其易使用的特点,它的性能超过sun公司官方的DOM技术。百度百科
DOM4J解析优缺点
特点:
- JDOM的一种智能分支,它合并了许多超出基本XML文档表示的功能。
- 它使用接口和抽象基本类方法。
- 具有性能优异.灵活性好.功能强大和极端易用的特点。
- 是一个开放源码的文件
DOM4J解析示例
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;
/**
* @author zhangshimin
*
*/
public class DOM4JReader {
/**
* @param args
*/
public static void main(String[] args) {
String xmlPath = "src/main/resources/books.xml";
List<Book> books = getBooksFromXML(xmlPath);
for (Book book : books) {
System.out.printf("bookId : %s %n", book.getId());
System.out.printf("\tname: %s %n", book.getName());
System.out.printf("\tauthor: %s %n", book.getAuthor());
System.out.printf("\tprice: %s %n", book.getPrice());
System.out.printf("\tpress: %s %n", book.getPress());
}
}
private static List<Book> getBooksFromXML(String xmlPath) {
List<Book> books = new ArrayList<>();
// 解析books.xml文件
// 创建SAXReader的对象reader
SAXReader reader = new SAXReader();
try {
// 通过reader对象的read方法加载books.xml文件,获取docuemnt对象。
Document document = reader.read(new File(xmlPath));
// 通过document对象获取根节点bookstore
Element bookStore = document.getRootElement();
// 通过element对象的elementIterator方法获取迭代器
Iterator<Element> it = bookStore.elementIterator();
// 遍历迭代器,获取根节点中的信息(书籍)
Book bookEntity = null;
Element book = null;
while (it.hasNext()) {
book = it.next();
bookEntity = new Book();
// 获取book的属性名以及 属性值
List<Attribute> bookAttrs = book.attributes();
for (Attribute attr : bookAttrs) {
if (attr.getName().equals("id")) {
bookEntity.setId(attr.getValue());
}
}
String eleName = null;
String eleValue = null;
Element bookChild = null;
Iterator<Element> itt = book.elementIterator();
while (itt.hasNext()) {
bookChild = itt.next();
eleName = bookChild.getName();
eleValue = bookChild.getStringValue();
if (bookChild.getName().equals("name")) {
bookEntity.setName(eleValue);
} else if (eleName.equals("author")) {
bookEntity.setAuthor(eleValue);
} else if (eleName.equals("price")) {
bookEntity.setPrice(eleValue);
} else if (eleName.equals("press")) {
bookEntity.setPress(eleValue);
}
}
books.add(bookEntity);
}
} catch (DocumentException e) {
e.printStackTrace();
}
return books;
}
}