今天我总结一下我学习xml解析的一些知识。
xml的解析方式有两种,一种是DOM,是一种基于文档驱动的解析方式。另一种是SAX,是一种基于事件的解析方式。
DOM解析
DOM的解析过程是先将需要解析的xml文档一次性读到内存,并创建Document树。可以随机获得任何节点信息。也可以修改Document树结构,即修改xml内容。其是一种面向对象的编程思想。缺点:由于一次性将文件的所有内容全读到了内存中,因此,当文件内容比较多时,很耗内存。
1、 读取过程(使用dom4j工具)
xml结构:
<?xml version="1.0" encoding="utf-8"?>
<studentList>
<student id="001">
<name>张三</name>
<age>20</age>
<stature>178</stature>
</student>
<student id="002">
<name>李四</name>
<age>24</age>
<stature>165</stature>
</student>
</studentList>
student.java
public class Student {
private final String id;
private String name;
private int age;
private double stature;
public Student(String id) {
this.id = id;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getStature() {
return stature;
}
public void setStature(double stature) {
this.stature = stature;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(id)
.append(" : ").append(name)
.append(" age : ").append(age)
.append(" stature : ").append(stature);
return sb.toString();
}
}
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.QName;
import org.dom4j.io.SAXReader;
public class DOMReader {
public static void main(String[] args) throws DocumentException {
// 获得xml文档的Document树结构
Document doc = new SAXReader().read(new File("./src/student.xml"));
// 获取根节点,即studentList
Element rootElem = doc.getRootElement();
// 获取根节点的所有子节点,即所有student节点
// 还可以通过rootElem.elementIterator();遍历所有子节点
List<Element> elems = rootElem.elements();
List<Student> stuList = new ArrayList<Student>();
for(Element elem : elems ) {
Student stu = new Student(elem.attributeValue("id"));
List<Element> childs = elem.elements();
for(Element e : childs ) {
final QName qName = e.getQName();
String value = e.getText();
switch( qName.getName() ) {
case "name":
stu.setName(value);
break;
case "age":
stu.setAge(Integer.valueOf(value));
break;
case "stature":
stu.setStature(Double.valueOf(value));
break;
default:
break;
}
}
stuList.add(stu);
}
for(Student stu : stuList ) {
System.out.println(stu);
}
}
}
运行结果如下:
2、修改xml内容
// 将document对象修改过后,直接写入文件就可以了
rootElem.setName("listStudent");
XMLWriter writer = new XMLWriter(
new FileOutputStream(new File("./src/student.xml")));
writer.write(doc);
writer.flush();
writer.close();
3、xpath技术
当xml文档的结构比较深时,对节点的查找非常麻烦,就可以用xpath解决这个问题,xpath依赖包不在dom4j的核心包中,因此要引入其依赖包 jaxen-1.1-beta-6.jar
使用xpath只有两个方法:
selectNodes(“xpath表达式”);查找符合条件的所有节点对象
selectSingleNode(“xpath表达式”);查找符合条件的单个节点对象
4、 xpath语法
/**
符号的含义:
/ 绝对路径 表示从xml的根位置开始,有层次结构
// 相对路径 表示不分层次结构的选择元素
* 通配符 表示匹配所有元素节点
@* 通配符 表示匹配所有的属性
node() 通配符 表示匹配任何类型的节点(比如属性)
[] 条件 表示选择符合条件的元素
@ 属性 表示选择属性节点
| 运算符 表示表示匹配多个路径
and 运算符 表示条件与(等价于&&)
or 运算符 表示条件或(等价于||)
有关函数:
last() 表示当前节点的最后一个子元素
text() 文本 表示选择文本内容
count() 表示计数符合条件的节点个数
name() 表示返回节点的名称
starts-with(,) 表示第一个字符串以第二个字符串开始
contains(,) 表示第一个字符串包含第二个字符串
string-length() 表示返回字符串的长度
normalize-space() 表示将前后空格删除,并将连续空格替换为单个空格
*/
SAX解析
SAX解析是基于事件的解析方式。即在遍历xml文件时,只有相应的事件被触发了,才会执行回调来取得数据。因此,其解析过程是从前往后的,只能顺序读取,不能回读。优点就是内存消耗较少。
1. 读取过程
// 由于SAX解析类内置在jdk中,所以不需要使用工具
// 核心类 SAXParser
SAXParser parser = SAXParserFactory.newInstance().newParser();
// 调用parse方法解析xml
// parse(File, DefaultHandler)
parser.parse(new File("./src/test.xml"), new MyDefaultHandler());
其主要就是自己继承解析回调的类DefaultHandler来实现解析时回调的方法。DefaultHandler实现了EntityResolver, DTDHandler, ContentHandler, ErrorHandler接口,但除了方法fatalError直接抛出异常外,其余的方法全部都是空的,没有任何操作。我们实现MyDefaultHandler类的时候,主要覆写的方法为ContentHandler接口中的startDocument, startElement, endElement, endDocument, characters。
下面我们看看具体例子:
MyDefaultHandler.java
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 MyDefaultHandler extends DefaultHandler{
private static final String attrId = "id";
private static final String nodeAge = "age";
private static final String nodeName = "name";
private static final String nodeStudent = "student";
private static final String nodeStature = "stature";
private static final String nodeStudentList = "studentList";
private Student stu;
private String tag;
private List<Student> myList;
public List<Student> getStudents() {
return myList;
}
// 在遇到<?xml version="1.0" encoding="utf-8"?>时调用该方法
@Override
public void startDocument() throws SAXException {
myList = new ArrayList<Student>();
}
// 在每个标签开始时调用
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if( qName.equals(nodeStudent) && attributes != null ) {
stu = new Student(attributes.getValue(attrId));
}
tag = qName;
}
// 获取每个标签的文本时调用
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String str = new String(ch, start, length);
if( stu != null && tag != null ) {
str = new String(ch, start, length);
switch( tag ) {
case nodeName:
stu.setName(str);
break;
case nodeAge:
stu.setAge(Integer.valueOf(str));
break;
case nodeStature:
stu.setStature(Double.valueOf(str));
break;
default:
break;
}
}
}
// 每个标签结束时调用
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if( qName.equals(nodeStudent) && stu != null ) {
myList.add(stu);
stu = null;
}
tag = null;
}
}
test类:
public class SAXReader {
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
SAXParser sax = SAXParserFactory.newInstance().newSAXParser();
MyDefaultHandler handler = new MyDefaultHandler();
sax.parse(new File("./src/student.xml"), handler);
List<Student> stuList = handler.getStudents();
for(Student stu : stuList ) {
System.out.println(stu);
}
}
}
运行结果如下: