XML简介
XML的作用
- 数据存储:
- 数据库提供了更强有力的数据存储和分析能力
- XML仅仅是存储数据
- XML与其他数据表现形式最大的不同是它极其简单,正是这点使XML与众不同
- 数据交换
- 由于各个计算机所使用的操作系统、数据库不同,因此数据之间的交换向来是件头痛的事
- 可以使用XML来交换数据
- 数据配置:
- 使用XML配制文件可读性强,灵活性高
解析XML
- 目前最常用的XML解析技术
- DOM
- SAX
- Sun公司提供了JAXP(JavaAPIforXML)接口来使用DOM和SAX
- org.w3c.dom:W3C推荐的用于使用DOM解析XML文档的接口
- org.xml.sax:用于使用SAX解析XML文档的接口
- javax.xml.parsers:解析器工厂工具,程序员获得并配置特殊的特殊语法分析器
两种解析方式的优缺点比较
Java的xml解析器库有很多,总的来说,万变不离其宗的就是SAX和DOM解析器。
- SAX的包是org.xml.sax
- DOM的包是org.w3c.dom
-
DOM
DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准。DOM是以层次结构组织的节点或信息片断的集合。这个层次结构允许开发人员在树中寻找特定信息。分析该结构通常需要加载整个文档和构造层次结构,然后才能做任何工作。由于它是基于信息层次的,因而DOM被认为是基于树或基于对象的。DOM以及广义的基于树的处理具有几个优点。首先,由于树在内存中是持久的,因此可以修改它以便应用程序能对数据和结构作出更改。它还可以在任何时候在树中上下导航,而不是像SAX那样是一次性的处理。DOM使用起来也要简单得多。
另一方面,对于特别大的文档,解析和加载整个文档可能很慢且很耗资源,因此使用其他手段来处理这样的数据会更好。这些基于事件的模型,比如SAX。 -
SAX
这种处理的优点非常类似于流媒体的优点。分析能够立即开始,而不是等待所有的数据被处理。而且,由于应用程序只是在读取数据时检查数据,因此不需要将数据存储在内存中。这对于大型文档来说是个巨大的优点。事实上,应用程序甚至不必解析整个文档;它可以在某个条件得到满足时停止解析。一般来说,SAX还比它的替代者DOM快许多。 -
选择DOM还是选择SAX?
对于需要自己编写代码来处理XML文档的开发人员来说,选择DOM还是SAX解析模型是一个非常重要的设计决策。DOM采用建立树形结构的方式访问XML文档,而SAX采用的事件模型。
DOM解析器把XML文档转化为一个包含其内容的树,并可以对树进行遍历。用DOM解析模型的优点是编程容易,开发人员只需要调用建树的指令,然后利用navigationAPIs访问所需的树节点来完成任务。可以很容易的添加和修改树中的元素。然而由于使用DOM解析器的时候需要处理整个XML文档,所以对性能和内存的要求比较高,尤其是遇到很大的XML文件的时候。由于它的遍历能力,DOM解析器常用于XML文档需要频繁的改变的服务中。
SAX解析器采用了基于事件的模型,它在解析XML文档的时候可以触发一系列的事件,当发现给定的tag的时候,它可以激活一个回调方法,告诉该方法制定的标签已经找到。SAX对内存的要求通常会比较低,因为它让开发人员自己来决定所要处理的tag。特别是当开发人员只需要处理文档中所包含的部分数据时,SAX这种扩展能力得到了更好的体现。但用SAX解析器的时候编码工作会比较困难,而且很难同时访问同一个文档中的多处不同数据。
- DOM:
解析器读入整个文档,然后构建一个驻留内存的树结构,然后代码就可以使用DOM接口来操作这个树结构。优点:整个文档树在内存中,便于操作;支持删除、修改、重新排列等多种功能;缺点:将整个文档调入内存(包括无用的节点),浪费时间和空间;使用场合:一旦解析了文档还需多次访问这些数据;硬件资源充足(内存、CPU) - SAX:
事件驱动。当解析器发现元素开始、元素结束、文本、文档的开始或结束等时,发送事件,程序员编写响应这些事件的代码,保存数据。优点:不用事先调入整个文档,占用资源少缺点:不是持久的;事件过后,若没保存数据,那么数据就丢了;无状态性;从事件中只能得到文本,但不知该文本属于哪个元素;使用场合:只需XML文档的少量内容,很少回头访问;一次性读取;机器内存少;注意:SAX解析器不创建任何对象。
- DOM:
下方的XML文件将作为我们的解析对象:
<?xml version="1.0" encoding="UTF-8"?>
<students>
<student id="1001">
<name>张三</name>
<age>22</age>
<sex>男</sex>
</student>
<student id="1002">
<name>李四</name>
<age>32</age>
<sex>男</sex>
</student>
</students>
XML解析方式
1.DOM解析
开始解析:
try {
// 1.获得文档解析工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 2.获得文档解析器
DocumentBuilder builder = factory.newDocumentBuilder();
// 3.解析XML文档,获得XML文档树
Document doc = builder.parse("student.xml");
// 4.获取文档的根结点
Element root = doc.getDocumentElement();
// 获取所有名称为student的结点元素,返回一个含有所有学生的列表
NodeList students = root.getElementsByTagName("student");
// 遍历列表
for (int i = 0; i < students.getLength(); i++) {
// 获得列表中的一个学生元素
Node student = students.item(i);
// 获得学生的id属性元素
Node id = student.getAttributes().getNamedItem("id");
// 获得属性元素的文本内容
System.out.println("学生id属性为: " + id.getTextContent());
// 获得一个学生元素的内部子节点,返回所有结点的列表
NodeList childNodes = student.getChildNodes();
// 遍历这个列表
for (int j = 0; j < childNodes.getLength(); j++) {
// 获得列表中的一个元素
Node item = childNodes.item(j);
// 获得元素的文本内容
String content = item.getNodeName();
if ("name".equals(content)) {
System.out.println("学生姓名为:" + item.getTextContent());
continue;
}
if ("age".equals(content)) {
System.out.println("学生年龄为:" + item.getTextContent());
continue;
}
if ("sex".equals(content)) {
System.out.println("学生性别为:" + item.getTextContent());
continue;
}
}
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
2.SAX解析
开始解析:
public class SaxParse {
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
// 1.SAX解析器工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
// 2.使用SAX解析器工厂,创建解析器
SAXParser parser = factory.newSAXParser();
// 3.创建事件处理对象
MyHandler handler = new MyHandler();
// 4.使用parse解析XML文档
parser.parse("student.xml", handler);
List<Student> list = handler.getStudentList();
System.out.println(list);
}
}
class MyHandler extends DefaultHandler {
private List<Student> list = new ArrayList<>();
private Student s = null;
/*
* 0:其他标签 1:name标签 2:age标签 3:sex标签
*/
private State state = State.NONE;
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
// 如果遇到不是name,age,sex的标签,跳过解析
if (state != State.NONE) {
String str = new String(ch, start, length);
System.out.println("获取对象 -> " + str);
switch (state) {
case NAME:
s.setName(str);
break;
case AGE:
s.setAge(Integer.parseInt(str));
break;
case SEX:
s.setSex(str);
break;
default:
break;
}
state = State.NONE;
}
}
/**
* 此发再SAX解析器开始解析XML文档时触发
*
* @throws SAXException
*/
@Override
public void startDocument() throws SAXException {
System.out.println("开始文档解析");
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if ("student".equals(qName)) {
s = new Student();
}
if (s != null) {
switch (qName) {
case "name":
state = State.NAME;
break;
case "age":
state = State.AGE;
break;
case "sex":
state = State.SEX;
break;
default:
break;
}
}
System.out.println("开始标签 -> " + qName);
if (attributes.getLength() > 0) {
for (int i = 0; i < attributes.getLength(); i++) {
String id = attributes.getValue("id");
s.setId(Integer.parseInt(id));
System.out.println("获取属性 -> " + attributes.getQName(i) + " = " + attributes.getValue(i));
}
}
}
@Override
public void endDocument() throws SAXException {
System.out.println("结束文档解析");
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
System.out.println("结束标签 -> " + qName);
if ("student".equals(qName)) {
list.add(s);
s = null;
}
}
public List<Student> getStudentList() {
return list;
}
}
enum State {
/**
* 未匹配到nage,age,sex结点
*/
NONE(0),
/**
* 解析到name结点
*/
NAME(1),
/**
* 解析到age结点
*/
AGE(2),
/**
* 解析到SEX结点
*/
SEX(3);
private int state;
private State(int state) {
this.state = state;
}
public int getState() {
return state;
}
}
3.DOM4J解析:
dom4j是一个Java的XML API,是jdom的升级品,用来读写XML文件的。dom4j是一个十分优秀的JavaXML API,具有性能优异、功能强大和极其易使用的特点,它的性能超过sun公司官方的dom技术,同时它也是一个开放源代码的软件,可以在SourceForge上找到它。在IBM developerWorks上面还可以找到一篇文章,对主流的Java XML API进行的性能、功能和易用性的评测,所以可以知道dom4j无论在哪个方面都是非常出色的。如今可以看到越来越多的Java软件都在使用dom4j来读写XML,特别值得一提的是连Sun的JAXM也在用dom4j。这已经是必须使用的jar包, Hibernate也用它来读写配置文件。
特征:
- JDOM的一种智能分支,它合并了许多超出基本XML文档表示的功能。
- 2、它使用接口和抽象基本类方法。
- 具有性能优异、灵活性好、功能强大和极端易用的特点。
- 是一个开放源码的文件
try {
// 创建解析器
SAXReader reader = new SAXReader();
// 解析xml文件
Document document = reader.read("student.xml");
// 获得根结点
Element root = document.getRootElement();
// 获得根结点的子节点
List<Element> students = root.elements();
// 迭代
for (Iterator<Element> it = students.iterator(); it.hasNext();) {
Element student = it.next();
System.out.println("学生id属性:" + student.attribute("id").getText());
// 获得子节点
List<Element> childNodes = student.elements();
for (Iterator<Element> itt = childNodes.iterator(); itt.hasNext();) {
Element child = itt.next();
// 获得结点名和对应结点的文本内容
System.out.println(child.getName() + " : " + child.getText());
}
}
} catch (DocumentException e) {
e.printStackTrace();
}
XML文档的序列化及反序列化
Xstream是一种OXMapping 技术,是用来处理XML文件序列化的框架,在将JavaBean序列化,或将XML文件反序列化的时候,不需要其它辅助类和映射文件,使得XML序列化不再繁索。Xstream也可以将JavaBean序列化成Json或反序列化,使用非常方便。使用前需要到官方下载指定的jar包.
序列化:
try (FileOutputStream oos = new FileOutputStream("students.xml");) {
Student[] ss = new Student[] {
new Student("张三", 22, "男"),
new Student("李四", 32, "男"),
};
XStream xStream = new XStream();
// 起别名
xStream.alias("所有学生", Student[].class);
xStream.alias("学生", Student.class);
xStream.aliasField("姓名", Student.class, "name");
xStream.aliasField("年龄", Student.class, "age");
xStream.aliasField("性别", Student.class, "sex");
xStream.toXML(ss, oos);
} catch (IOException e) {
e.printStackTrace();
}
输出students.xml文件:
<所有学生>
<学生>
<姓名>张三</姓名>
<年龄>22</年龄>
<性别>男</性别>
</学生>
<学生>
<姓名>李四</姓名>
<年龄>32</年龄>
<性别>男</性别>
</学生>
</所有学生>
反序列化:
try (FileInputStream fis = new FileInputStream("src/test/students.xml")) {
XStream xStream = new XStream();
xStream.alias("所有学生", Student[].class);
xStream.alias("学生", Student.class);
xStream.aliasField("姓名", Student.class, "name");
xStream.aliasField("年龄", Student.class, "age");
xStream.aliasField("性别", Student.class, "sex");
Student[] students = (Student[])xStream.fromXML(fis);
System.out.println(Arrays.toString(students));
} catch (IOException e) {
e.printStackTrace();
}