Java基础系列-XML文件解析

XML解析模型

解析XML文件一般有两种模型,一种是DOM(文档)模型,另一种是流模型。

DOM模型

DOM的全称是Document Object Model,也即文档对象模型,它是与平台无关的。它是将XML文件转换为一个对象文档模型(即DOM树),然后程序可以对该对象文档模型进行操作,进而操作XML中的数据。

DOM解析优缺点
优点:

  1. 形成树结构,有助于更好的理解、掌握,并且编写代码比较容易
  2. 解析过程中,文档结构保存在内存中,方便读取,方便修改

不足:

  1. 一次性读取到内存中,所以内存消耗很大
  2. 对于大文件解析可能有性能损失,可能会造成内存溢出

流模式

流模式解析文档的时候与DOM模式有很大不同,它不会一次性将所有内容装入内存,而是从头部开始一段一段处理。
优点:

  1. 对内存耗费比较小
  2. 适用于只处理XML文件中的数据时

不足:

  1. 很难同时访问XML文件中的多处不同数据
  2. 不能编辑
  3. 只能前进不能后退
事件类型

javax.xml.stream.XMLStreamConstants中可以看到;下表中也有部分表示

名称意义
START_ELEMENT1元素开始
END_ELEMENT2元素结束
PROCESSING_INSTRUCTION3处理指令
CHARACTERS4字符(文本或空格)
COMMENT5注释
SPACE6可忽略空格
START_DOCUMENT7文档开始
END_DOCUMENT8文档结束
ENTITY_REFERENCE9实体引用
ATTRIBUTE10元素属性
DTD11DTD
CDATA12CDATA
NAMESPACE13命名空间
NOTATION_DECLARATION14标记声明
ENTITY_DECLARATION15实体声明

在流模式中有两种处理,一种是推模式(SAX),一种是拉模式(STAX),简单介绍一下这两种模式。

SAX

SAX方式不是标准的W3C标准,但是它是一个广泛使用的API。SAX的解析器不是在内存中建立DOM数结构,而是在读取文件时触发一系列的事件。这些事件推给事件处理器,由事件处理器处理。

  1. DTDHandler接口 用于访问XML DTD内容的
  2. ErrorHandler接口 用于低级访问解析错误的
  3. 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解析优缺点

特点:

  1. 仅使用具体类而不使用接口。这在某些方面简化了 API,但是也限制了灵活性。
  2. 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解析优缺点

特点:

  1. JDOM的一种智能分支,它合并了许多超出基本XML文档表示的功能。
  2. 它使用接口和抽象基本类方法。
  3. 具有性能优异.灵活性好.功能强大和极端易用的特点。
  4. 是一个开放源码的文件
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;
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值