XML 简介
XML 指可扩展标记语言(EXtensible Markup Language), 是一种很像HTML的标记语言,但是XML用于传输和存储数据的.
XML的解析方式分有:(1)DOM解析;(2)SAX解析;(3)STAX解析;(4)JDOM解析;(5)DOM4J解析。其中前两种属于基础方法,是官方提供的平台无关的解析方式;后两种属于扩展方法,它们是在基础的方法上扩展出来的,只适用于java平台。
针对以下的XML 文件,分别采用以上提到的五种方式进行解析:
<?xml version="1.0" encoding="UTF-8"?>
<framily>
<memeber id="1">
<Seniority >grndpa</Seniority>
<age>75</age>
<hobby>Writing brush</hobby>
</memeber>
<memeber id="2">
<Seniority >grndma</Seniority>
<age>70</age>
<hobby>Read book</hobby>
</memeber>
<memeber id="3">
<Seniority >father</Seniority>
<age>48</age>
<hobby>Play golf</hobby>
</memeber>
<memeber id="4">
<Seniority >mom</Seniority>
<age>45</age>
<hobby>Grow flowers</hobby>
</memeber>
<memeber id="5">
<Seniority >son</Seniority>
<age>20</age>
<hobby>Play basketball</hobby>
</memeber>
</framily>
DOM 解析
DOM模式解析XML,是把整个XML文档当成一个对象来处理,会先把整个文档读入到内存里。是基于树的结构,通常需要加载整文档和构造DOM树,然后才能开始工作。
@Test
public void domTest() throws ParserConfigurationException, IOException, SAXException {
//创建待解析的XML文件,并指定目标
File file=new File("src/main/resources/xmlanalysisTest.xml");
//1.创建DocumentBuilderFactory
DocumentBuilderFactory documentBuilderFactory= DocumentBuilderFactory.newInstance();
// 2.用工厂对象创建DocumentBuilder对象
DocumentBuilder documentBuilder=documentBuilderFactory.newDocumentBuilder();
// 3.用DocumentBuilder对象将xml文件加载进内存,用Document对象接收
Document document= documentBuilder.parse(file);
// 4.获取到所有family节点,返回NodeList
NodeList memberList= document.getElementsByTagName("memeber");
// 5.循环遍历NodeList里的family节点
/* for (int i=0; i<memberList.getLength();i++) {
//将每个Person 节点又转为元素节点(标签节点)
Node member = memberList.item(i);
//得到属性的集合
NamedNodeMap namedNodeMap = member.getAttributes();
//循环遍历每一个属性
for (int j = 0; j < namedNodeMap.getLength(); j++) {
Node id = namedNodeMap.item(j);
System.out.println("属性的名称:" + id.getNodeName() + "\t" + id.getNodeValue());
}
}*/
System.out.println("\n每个节点的名和节点的值");
for (int i=0;i<memberList.getLength();i++){
Node member= memberList.item(i);
System.out.println("id:"+member.getAttributes().getNamedItem("id").getNodeValue());
NodeList subNode= member.getChildNodes();
for (int j=0 ;j<subNode.getLength();j++){
Node childNode = subNode.item(j);
short type = childNode.getNodeType();
if(type==Node.ELEMENT_NODE){
System.out.println("节点的名称:"+childNode.getNodeName()+"\t"+childNode.getTextContent());
}
}
}
}
优点:
- 允许应用程序对数据和结构做出更改。
- 访问是双向的,可以在任何时候在树中上下导航,获取和操作任意部分的数据。
- 整个文档树在内存中,便于操作;支持删除、修改、重新排列等多种功能
缺点:
- 整个 XML 文档必须一次性解析完,耗时。
- 将整个文档调入内存(包括无用的节点),浪费时间和空间;
使用场合:文档较小,且需要修改文档内容
SAX 解析
SAX的全称是Simple APIs for XML,也即XML简单应用程序接口。SAX处理的特点是基于事件流的。分析能够立即开始,而不是等待所有的数据被处理。而且,由于应用程序只是在读取数据时检查数据,因此不需要将数据存储在内存中。这对于大型文档来说是个巨大的优点。事实上,应用程序甚至不必解析整个文档;它可以在某个条件得到满足时停止解析。sax分析器在对xml文档进行分析时,触发一系列的事件,应用程序通过事件处理函数实现对xml文档的访问,因为事件触发是有时序性的,所以sax分析器提供的是一种对xml文档的顺序访问机制,对于已经分析过的部分,不能再重新倒回去处理。
SAX解析方式:逐行扫描文档,一边扫描一边解析。相比于DOM,SAX可以在解析文档的任意时刻停止解析解析,是一种速度更快,更高效的方法。
SAX 是推模式的,所有的操作在解析器自动控制下进行,所有事件都会处理,不管需不需要解析整个文档,解析器都会自动启动解析任务,然后按顺序向下解析,直到解析完成才终止。 简单说来,推模式,就是你把如何操作通通告诉解析器,之后,解析器自动去完成所有解析任务,你不能做任何干涉了。
public class FamilyHandler extends DefaultHandler {
@Override
public void startDocument() throws SAXException {
super.startDocument();
System.out.println("解析XML 文件开始");
}
@Override
public void endDocument() throws SAXException {
super.endDocument();
System.out.println("解析XML 文件结束");
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
if("member".equals(qName)){
int length = attributes.getLength();
//遍历属性
for (int i=0;i<length;i++){
String name = attributes.getQName(i);
String value = attributes.getValue(i);
System.out.println("属性名:"+name+"\t属性值:"+value);
}
}else {
System.out.print("节点名:"+qName+"\t");
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
}
/**
* 获取节点值
* @param ch
* @param start
* @param length
* @throws SAXException
*/
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
String value= new String(ch,start,length);
if(!"".equals(value.trim())){
System.out.println("节点值:"+value);
}
}
}
@Test
public void saxTest() throws ParserConfigurationException, SAXException, IOException {
//(1)创建SAXParserFactory对象
SAXParserFactory saxParserFactory =SAXParserFactory.newInstance();
//(2)通过SAXParserFactory对象创建SAXParser对象
SAXParser parser = saxParserFactory.newSAXParser();
//(3)创建DefaultHandler的子类
FamilyHandler bookDefaultHandler = new FamilyHandler();
//(4)调用parse()方法
parser.parse("src/main/resources/xmlanalysisTest.xml",bookDefaultHandler);
}
优点:
- 访问能够立即进行,不需要等待所有数据被加载。
- 只在读取数据时检查数据,不需要保存在内存中
- 不需要将整个数据都加载到内存中,占用内存少
- 允许注册多个Handler,可以用来解析文档内容,DTD约束等等。
缺点:
- 需要应用程序自己负责TAG的处理逻辑(例如维护父/子关系等),文档越复杂程序就越复杂。
- 单向导航,无法定位文档层次,很难同时访问同一文档的不同部分数据,不支持XPath。
- 不能随机访问 xml 文档,不支持原地修改xml。
适用于:文档较大,只需要读取文档数据。
STAX 解析
Java 6.0对XML支持的新特性有许多方面。比如StAX、针对XML-Web服务的Java架构(JAX-WS)2.0、针对XML绑定的API(JAXB)2.0、XML数字签名API,甚至还支持SQL:2003 'XML'数据类型。
StAX是Streaming API for XML的缩写,是一种针对XML的流式拉分析API。StAX是一个基于JAVA API用于解析XML文档,类似SAX解析器的方式
StAX 是拉模式的,可以由用户根据需要控制需要处理事件类型及何时终止解析任务,用户调用一次next(),解析器就进行一次向下解析,完全在用户的控制下进行解析操作,在需要的解析工作已经完成时,可以随时终止解析。简单来说,拉模式就像抽纸,随用随拉,需要时就拉一下,不需要时,它不会自动推出来。你可以一直拉把一盒抽纸拉完,也可以按自己需要只拉几张。
StAX的API可以读取和写入XML文档。
@Test
public void StaxTest() throws FileNotFoundException, XMLStreamException {
//创建XML流工程
XMLInputFactory factory = XMLInputFactory.newInstance();
InputStream resourceAsStream = new FileInputStream("src/main/resources/xmlanalysisTest.xml");
//创建XML事件读取流
XMLEventReader reader = factory.createXMLEventReader(resourceAsStream);
while (reader.hasNext()){
XMLEvent event = reader.nextEvent();
//如果是开始元素
if (event.isStartElement()){
//读取开始元素
StartElement startElement = event.asStartElement();
String name= startElement.getName().toString();
// 获取属性
Iterator iterator = startElement.getAttributes();
while (iterator.hasNext()){
Attribute obj= (Attribute) iterator.next();
System.out.println(obj.getName() +":" +obj.getValue());
}
System.out.println("<" + name +">");
}
//如果是文本
if (event.isCharacters()){
//获取文本值
String str= event.asCharacters().getData();
if(!"".equals(str.trim())){
System.out.println(str);
}
}
}
}
应该使用的StAX解析器的时候:
- 可以处理在自上而下线性方式的XML文档。
- 文件并不深入嵌套。
- 处理一个非常大的XML文档的DOM树会占用太多的内存。典型的DOM的实现使用10字节的存储器以表示XML的一个字节。
- 要解决的问题涉及XML文档的一部分。
- 数据是可用的,只要它是由解析器处理,这样StAX可以很好地用于所收到超过数据流的XML文档。
SAX的缺点
因为它是在一个处理的方式,而不是随机访问XML文档。
如果需要跟踪的数据分析器已经看到或更改项目的顺序,必须编写代码和数据存储以自己方式处理。
JDOM 解析
JDOM是处理xml的纯java api.使用具体类而不是接口.JDOM具有树的遍历,又有SAX的java规则.JDOM与DOM主要有两方面不同。
首先,JDOM仅使用具体类而不使用接口。这在某些方面简化了API,但是也限制了灵活性。
第二,API大量使用了Collections类,简化了那些已经熟悉这些类的Java开发者的使用。
JDOM自身不包含解析器。它通常使用SAX2解析器来解析和验证输入XML文档(尽管它还可以将以前构造的DOM表示作为输入)。它包含一些转换器以将JDOM表示输出成SAX2事件流、DOM模型或XML文本文档。
@Test
public void jbomTest() throws IOException, JDOMException {
//(1)创建SAXBuilder对象
SAXBuilder saxBuilder = new SAXBuilder();
//(2)调用build方法获得Document对象
Document document = saxBuilder.build(new FileInputStream("src/main/resources/xmlanalysisTest.xml"));
//(3)获取根节点
Element books = document.getRootElement();
//(4)获取根节点的直接子节点集合
List<Element> booksChildren = books.getChildren();
//(5)遍历
for(int i = 0; i < booksChildren.size(); i++){
//获取book节点
Element member = booksChildren.get(i);
//获取属性的集合
List<Attribute> attributes = member.getAttributes();
//遍历属性
for(Attribute attr : attributes){
System.out.println("属性名:"+attr.getName()+"\t属性值:"+attr.getValue());
}
}
System.out.println("--------------------------------------------");
for(int i = 0; i < booksChildren.size(); i++){
//获取book节点
Element member = booksChildren.get(i);
//获取book的子节点
List<Element> children = member.getChildren();
//遍历节点
for(Element ele : children){
System.out.println("节点名:"+ele.getName()+"\t节点值:"+ele.getValue());
}
}
}
优点:
1、是基于树的处理xml的java api,把树加载到内存中.
2、没有向下兼容的限制,所以比DOM简单.
3、速度快.
4、具有SAX的java 规则.
缺点:
1、不能处理大于内存的文档.
2、JDOM表示XML文档逻辑模型,不能保证每个字节真正变换.
3、 针对实例文档不提供DTD与模式的任何实际模型.
4、 不支持于DOM中相应遍历包.
DOM4J 解析
DOM4J有更复杂的api,所以dom4j比jdom有更大的灵活性.DOM4J性能最好,连Sun的JAXM也在用DOM4J.目前许多开源项目中大量采用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J来读取XML配置文件。如果不考虑可移植性,那就采用DOM4J.
先在maven中引入jar包:
<!--引用dom4j解析xml-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
创建实体类
public class Family {
private String id;
private String Seniority;
private String age;
private String hobby;
public String getId() {
return id;
}
public Family(String id, String seniority, String age, String hobby) {
this.id = id;
Seniority = seniority;
this.age = age;
this.hobby = hobby;
}
@Override
public String toString() {
return "Family{" +
"id='" + id + ''' +
", Seniority='" + Seniority + ''' +
", age='" + age + ''' +
", hobby='" + hobby + ''' +
'}';
}
public void setId(String id) {
this.id = id;
}
public String getSeniority() {
return Seniority;
}
public void setSeniority(String seniority) {
Seniority = seniority;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
}
dom4j解析xml:
@Test
public void dom4jTest() throws DocumentException {
// 1.创建SAXReader对象,
SAXReader saxReader = new SAXReader();
//2.读取XML文件
Document read = saxReader.read("src/main/resources/xmlanalysisTest.xml");
//3.通过Document 对象获取根元素
Element rootElement = read.getRootElement();
//4. 通过根元素获取book
//element() 和elements()都可以获取标签签名查找子元素
List<Element> family = rootElement.elements("memeber") ;
//5.遍历,每一个book标签转换为family类
for(Element memeber: family){
String Seniority=memeber.elementText("Seniority");
String age = memeber.elementText("age");
String hobby= memeber.elementText("hobby");
String id = memeber.attributeValue("id");
System.out.println(new Family(id,Seniority,age,hobby));
System.out.println("=================================");
}
}
优点:
- 大量使用了Java集合类,方便Java开发人员,同时提供一些提高性能的替代方法。
- 支持XPath。查找节点特别快
- 灵活性高。
缺点:
- 大量的使用了接口,API复杂,理解难。
- 移植性差。
解析方式对比
DOM4J性能最好,连Sun的JAXM也在用DOM4J。目前许多开源项目中大量采用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J来读取XML配置文件。如果不考虑可移植性,那就采用DOM4J。
JDOM和DOM在性能测试时表现不佳,在测试10M文档时内存溢出,但可移植。在小文档情况下还值得考虑使用DOM和JDOM.虽然JDOM的开发者已经说明他们期望在正式发行版前专注性能问题,但是从性能观点来看,它确实没有值得推荐之处。另外,DOM仍是一个非常好的选择。DOM实现广泛应用于多种编程语言。它还是许多其它与XML相关的标准的基础,因为它正式获得W3C推荐(与基于非标准的Java模型相对),所以在某些类型的项目中可能也需要它(如在JavaScript中使用DOM)。
SAX表现较好,这要依赖于它特定的解析方式-事件驱动。一个SAX检测即将到来的XML流,但并没有载入到内存(当然当XML流被读入时,会有部分文档暂时隐藏在内存中)。
总结:如果XML文档较大且不考虑移植性问题建议采用DOM4J;如果XML文档较小则建议采用JDOM;如果需要及时处理而不需要保存数据则考虑SAX