XML 是一种通用的数据交换格式, 它的平台无关, 语言无关, 系统无关, 在不同的语言环境的解析方式都是一样的, 只不过是实现的语法不同.
DOM ,SAX 属于基础方法, 是官方提供的平台无关的解析方式; JDOM,DOM4J 属于扩展方法, 他们是在基础的方法上扩展出来, 只适用于 Java 平台;
JAXP 是 SDK 提供的一套解析 XML 的 API, 支持 DOM 和 SAX 解析方式, JAXP 是 JavaSE 一部分, 它由 javax.xml,org.w3c.sax,org.xml.sax 包及其子包组成. 从 JDK6.0 开始, JAXP 开始支持另一种 XML 解析方式 --StAX 解析方式.
DOM 的全称是 Document Object Model, 也即文档对象模型. 应用程序中, 基于 DOM 的 XML 分析器将一个 XML 文档转换成一个对象模型的集合 (通常称 DOM 树), 应用程序正是通过对这个对象模型的操作, 来实现对 XML 文档数据的操作. 通过 DOM 接口, 应用程序可以在任何时候访问 XML 文档中的任何一部分数据, 因此, 这种利用 DOM 接口的机制也被称作随机访问机制.
DOM 接口提供了一种通过分层对象模型来访问 XML 文档信息的方式, 这些分层对象模型依据 XML 的文档结构形成了一棵节点树.
DOM 树所提供的随机访问方式给应用程序的开发带来了很大的灵活性, 它可以任意地控制整个 XML 文档中的内容. 然而, 由于 DOM 分析器把整个 XML 文档转化成 DOM 树放在了内存中, 因此, 当文档比较大或者结构比较复杂时, 对内存的需求就比较高. 而且, 对于结构复杂的树的遍历也是一项耗时的操作. 所以, DOM 分析器对机器性能的要求比较高, 实现效率不十分理想. 不过, 由于 DOM 分析器所采用的树结构的思想与 XML 文档的结构相吻合, 同时鉴于随机访问所带来的方便, 因此, DOM 分析器还是有很广泛的使用价值的.
优点:
1, 形成了树结构, 有助于更好的理解, 掌握, 且代码容易编写.
2, 解析过程中, 树结构保存在内存中, 方便修改.
缺点:
1, 由于文件是一次性读取, 所以对内存的耗费比较大.
2, 如果 XML 文件比较大, 容易影响解析性能且可能会造成内存溢出.
代码实例:
- public class DOMTest {
- public static void main(String[] args) {
- // 创建一个 DocumentBuilderFactory 的对象
- DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
- // 创建一个 DocumentBuilder 的对象
- try {
- // 创建 DocumentBuilder 对象
- DocumentBuilder db = dbf.newDocumentBuilder();
- // 通过 DocumentBuilder 对象的 parser 方法加载 books.xml 文件到当前项目下
- Document document = db.parse("books.xml");
- // 获取所有 book 节点的集合
- NodeList bookList = document.getElementsByTagName("book");
- // 通过 nodelist 的 getLength() 方法可以获取 bookList 的长度
- System.out.println("一共有" + bookList.getLength() + "本书");
- // 遍历每一个 book 节点
- for (int i = 0; i < bookList.getLength(); i++) {
- 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(index) 方法获取 book 节点的某一个属性
- Node attr = attrs.item(j);
- // 获取属性名
- System.out.print("属性名:" + attr.getNodeName());
- // 获取属性值
- System.out.println("-- 属性值" + attr.getNodeValue());
- }
- // 解析 book 节点的子节点
- NodeList childNodes = book.getChildNodes();
- // 遍历 childNodes 获取每个节点的节点名和节点值
- System.out.println("第" + (i+1) + "本书共有" +
- childNodes.getLength() + "个子节点");
- for (int k = 0; k < childNodes.getLength(); k++) {
- // 区分出 text 类型的 node 以及 element 类型的 node
- if (childNodes.item(k).getNodeType() == Node.ELEMENT_NODE) {
- // 获取了 element 类型节点的节点名
- System.out.print("第" + (k + 1) + "个节点的节点名:"
- + childNodes.item(k).getNodeName());
- // 获取了 element 类型节点的节点值
- System.out.println("-- 节点值是:" + childNodes.item(k).getFirstChild().getNodeValue());
- //System.out.println("-- 节点值是:" + childNodes.item(k).getTextContent());
- }
- }
- System.out.println("====================== 结束遍历第" + (i + 1) + "本书的内容 =================");
- }
- } catch (ParserConfigurationException e) {
- e.printStackTrace();
- } catch (SAXException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
二, SAX 解析:
SAX 全称 Simple APIs for XML, 也即 XML 简单应用程序接口. 与 DOM 不同, SAX 提供的访问模式是一种顺序模式, 这是一种快速读写 XML 数据的方式. 当使用 SAX 分析器对 XML 文档进行分析时, 会触发一系列事件, 并激活相应的事件处理函数, 应用程序通过这些事件处理函数实现对 XML 文档的访问, 因而 SAX 接口也被称作事件驱动接口.
SAX 的缺点也非常明显, 因为不存储 XML 文挡的结构, 所以需要开发人员自己负 责维护业务逻辑涉及的多层节点之间的关系, 例如, 某节点与其父节点之间的父子关系, 与其子节点之间的父子关系 . 当 XML 文档非常复杂时, 维护节点间关系的复杂度较高, 工作量也就会 比较大 . 另一方面, 因为是流式处理, 所以处理过程只能从 XML 文档开始向后单向进行, 无法像 DOM 方式那样 , 自由导航到之前处理过的节 点上重新处理, 也无法支持 XPath. SAX 没有提供写 XML 文档的功能 .
SAX 解析流程图
优点:
1, 采用事件驱动模式, 对内存耗费比较小.
2, 适用于只处理 XML 文件中的数据时.
缺点:
1, 编码比较麻烦.
2, 很难同时访问 XML 文件中的多处不同数据.
代码实例:
- public class SAXTest {
- public static void main(String[] args) {
- SAXParserFactory factory = SAXParserFactory.newInstance();
- try { SAXParser parser = factory.newSAXParser();
- SAXParserHandler handler = new SAXParserHandler();
- parser.parse("books.xml", handler);
- for (Book book : handler.getBookList()) {
- System.out.println(book.getId());
- System.out.println(book.getName());
- System.out.println(book.getAuthor());
- System.out.println(book.getYear());
- System.out.println(book.getPrice());
- System.out.println(book.getLanguage());
- System.out.println("----finish----"); }
- } 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(); } }}
- public class SAXParserHandler extends DefaultHandler { String value = null; Book book = null; private ArrayListbookList = new ArrayList(); public ArrayList getBookList() {
- return bookList;
- }
- int bookIndex = 0;
- /
用来标识解析开始
- /
- @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 解析结束");
- }
- /
解析 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")) {
- bookIndex++;
- // 创建一个 book 对象
- book = new Book();
- // 开始解析 book 元素的属性
- System.out.println("====================== 开始遍历某一本书的内容 =================");
- // 不知道 book 元素下属性的名称以及个数, 如何获取属性名以及属性值
- int num = attributes.getLength();
- for(int i = 0; i < num; i++){
- System.out.print("book 元素的第" + (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("name") && !qName.equals("bookstore")) {
- System.out.print("节点名是:" + qName + "---");
- }
- }
- @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;
- 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);
- }
- else if (qName.equals("price")) {
- book.setPrice(value);
- }
- else if (qName.equals("language")) {
- book.setLanguage(value);
- }
- }
- @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);
- if (!value.trim().equals("")) {
- System.out.println("节点值是:" + value);
- }
- }
- }
三, StAX 解析
StAX 实际上包括两套处理 XML 文档的 API, 分别提供了不同程度的抽象. 一种是基于指针的 API, 这是一种底层的 API, 效率高单抽象程度低. 另一种是基于迭代器的 API, 他允许应用程序把 XML 文档作为一系列事件对象来处理, 效率较低但抽象程度高.
StAX 解析流程
四, JDOM 解析
特征:
1, 仅使用具体类, 而不使用接口.
2,API 大量使用了 Collections 类.
代码实例:
- public class JDOMTest {
- private static ArrayListbooksList = new ArrayList();
- /
- @param args
- /
- public static void main(String[] args) {
- // 进行对 books.xml 文件的 JDOM 解析 // 准备工作 // 1. 创建一个 SAXBuilder 的对象
- SAXBuilder saxBuilder = new SAXBuilder();
- InputStream in;
- try {
- // 2. 创建一个输入流, 将 xml 文件加载到输入流中
- in = new FileInputStream("src/res/books.xml");
- 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 集合 ListbookList = rootElement.getChildren();
- // 继续进行解析
- for (Element book : bookList) {
- Book bookEntity = new Book();
- System.out.println("====== 开始解析第" + (bookList.indexOf(book) + 1) + "书 ======");
- // 解析 book 的属性集合
- Lis tattrList = book.getAttributes();
- //// 知道节点下属性名称时, 获取节点值
- // book.getAttributeValue("id");
- // 遍历 attrList(针对不清楚 book 节点下属性的名字及数量)
- for (Attribute attr : attrList) {
- // 获取属性名
- String attrName = attr.getName();
- // 获取属性值
- String attrValue = attr.getValue();
- System.out.println("属性名:" + attrName + "---- 属性值:" + attrValue);
- if (attrName.equals("id"))
- { bookEntity.setId(attrValue); }
- }
- // 对 book 节点的子节点的节点名以及节点值的遍历
- List bookChilds = book.getChildren();
- for (Element child : bookChilds) {
System.out.println("节点名:" + child.getName() + "---- 节点值:+ child.getValue());
- if (child.getName().equals("name")) {
- bookEntity.setName(child.getValue());
- }
- else if (child.getName().equals("author")) {
- bookEntity.setAuthor(child.getValue());
- }
- else if (child.getName().equals("year")) {
- bookEntity.setYear(child.getValue());
- }
- else if (child.getName().equals("price")) {
- bookEntity.setPrice(child.getValue());
- }
- else if (child.getName().equals("language")) {
- bookEntity.setLanguage(child.getValue());
- }
- }
- System.out.println("====== 结束解析第" + (bookList.indexOf(book) + 1)
- + "书 ======");
- booksList.add(bookEntity);
- bookEntity = null;
- System.out.println(booksList.size());
- System.out.println(booksList.get(0).getId());
- System.out.println(booksList.get(0).getName());
- }
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (JDOMException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
4,DOM4J 解析
特征:
1,DOM4J 的一种智能分支, 它合并了许多超出基本 XML 文档表示的功能.
2, 它使用接口和抽象基本类方法.
3, 具有性能优异, 灵活性好, 功能强大和极端易用的特点.
4, 是一个开放源码的文件
代码实例:
- public class DOM4JTest {
- private static ArrayListbookList = new ArrayList();
- / @param args /
- public static void main(String[] args) {
- // 解析 books.xml 文件
- // 创建 SAXReader 的对象 reader SAXReader reader = new SAXReader();
- try {
- // 通过 reader 对象的 read 方法加载 books.xml 文件, 获取 docuemnt 对象.
- Document document = reader.read(new File("src/res/books.xml"));
- // 通过 document 对象获取根节点
- bookstore Element bookStore = document.getRootElement();
- // 通过 element 对象的 elementIterator 方法获取迭代器
- Iterator it = bookStore.elementIterator();
- // 遍历迭代器, 获取根节点中的信息 (书籍)
- while (it.hasNext()) { System.out.println("===== 开始遍历某一本书 ====="); Element book = (Element) it.next();
- // 获取 book 的属性名以及 属性值 List bookAttrs = book.attributes();
- for (Attribute attr : bookAttrs) {
- System.out.println("属性名:" + attr.getName() + "-- 属性值:"
- + attr.getValue());
- }
- Iterator itt = book.elementIterator();
- while (itt.hasNext()) {
- Element bookChild = (Element) itt.next();
- System.out.println("节点名:" + bookChild.getName() + "-- 节点值:" + bookChild.getStringValue());
- }
- System.out.println("===== 结束遍历某一本书 =====");
- }
- } catch (DocumentException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
总结:
DOM4J 性能最好, 连 Sun 的 JAXM 也在用 DOM4J. 目前许多开源的项目中大量采用 DOM4J, 例如大名鼎鼎的 Hibernate 也用 DOM4J 来读取 XML 配置文件. 如果不考虑可移植性, 那就采用 DOM4J.
JDOM 和 DOM 在性能测试时表现不佳, 在测试 10M 文档时内存溢出. 在小文档的情况下还值得考虑使用 DOM 和 JDOM. 另外 DOM 仍是一个非常好的选择, DOM 实现广泛应用于多种编程语言, 它还是许多其他与 Xml 相关的标准基础, 因为它正式获得 W3C 推荐, 所以在某些类型的项目中可能也需要它 (如 JavaScript 中使用 DOM).SAX 表现较好, 这要依赖于它特定的解析方式 -- 事件驱动. 一个 SAX 检测即将到来的 XML 流, 但并没有载入到内存 (当然当 XML 流被读入时, 会有部分文档暂时隐藏在内存中).
来源: http://www.jianshu.com/p/9d6e5b066908