什么是XML解析
前面XML章节已经说了,XML被设计为“什么都不做”,XML只用于组织、存储数据,除此之外的数据生成、读取、传送等等的操作都与XML本身无关!
XML解析就是读取XML的数据!
XML解析方式
XML解析方式分为两种:
①:dom(Document Object Model)文档对象模型,是W3C组织推荐解析XML的一种方式
②:sax(Simple API For XML),它是XML社区的标准,几乎所有XML解析器都支持它!
XML解析操作
从上面的图很容易发现,应用程序不是直接对XML文档进行操作的,而是由XML解析器对XML文档进行分析,然后应用程序通过XML解析器所提供的DOM接口或者SAX接口对分析结果进行操作,从而间接地实现了对XML文档的访问!
常用的解析器和解析开发包的关系如下所示:
为什么有3种开发包?
- jaxp开发包是JDK自带的,不需要导入开发包。
- 由于sun公司的jaxp不够完善,于是就被研发了Jdom。XML解析如果使用Jdom,需要导入开发包
- dom4j是由于Jdom的开发人员出现了分歧,dom4j由Jdom的一批开发人员所研发。XML解析如果使用Jdom,需要导入开发包【现在用dom4j是最多的!】
jaxp
虽然jaxp解析XML的性能以及开发的简易度是没有dom4j好,但是jaxp不管怎么说都是JDK内置的开发包,我们是需要学习的!
DOM解析操作
DOM解析是一个基于对象的API,它把XML的内容加载到内存中,生成与XML文档内容对应的模型!当解析完成,内存中会生成与XML文档的结构与之对应的DOM对象树,这样就能够根据树的结构,以节点的形式对文档进行操作!
简单来说:DOM解析会把XML文档加载到内存中,生成DOM树的元素都是以对象的形式存在的!我们操作这些对象就能够操作XML文档了!
- 下面这样图就能很好地说明了,是怎么样生成与XML文档内容对应的DOM树!
既然XML文档的数据是带有关系型的,那么生成的DOM树的节点也是有关系的:
- 位于一个节点之上的节点是该节点的父节点(parent)
- 一个节点之下的节点是该节点的子节点(children)
- 同一层次,具有相同父节点的节点是兄弟节点(sibling)
- 一个节点的下一个层次的节点集合是节点后代(descendant)
- 父、祖父节点及所有位于节点上面的,都是节点的祖先(ancestor)
在DOM解析中有几个核心的操作接口:
- Document【代表整个XML文档,通过Document节点可以访问XML文件中所有的元素内容!】
- Node【Node节点几乎在XML操作接口中几乎相当于普通Java类的Object,很多核心接口都实现了它,在下面的关系图可以看出!】
- NodeList【代表着一个节点的集合,通常是一个节点中子节点的集合!】
- NameNodeMap【表示一组节点和其唯一名称对应的一一对应关系,主要用于属性节点的表示(书上说是核心的操作接口,但我好像没用到!呃呃呃,等我用到了,我再来填坑!)】
节点之间的关系图:
- 有人可能会很难理解,为什么Document接口比Node接口还小,呃呃呃,我是这样想的:一个Document由无数个Node组成,这样我也能把Document当然是Node呀!如果实在想不通:人家都这样设计了,你有种就不用啊!!(开玩笑的…..)
好的,不跟你们多bb,我们来使用一下Dom的方式解析XML文档吧!
- XML文档代码
<?xml version="1.0" encoding="UTF-8" ?>
<china>
<guangzhou >广州</guangzhou>
<shenzhen>深圳</shenzhen>
<beijing>北京</beijing>
<shanghai>上海</shanghai>
</china>
- 根据XML解析的流程图,我们先要获取到解析器对象!
public class DomParse {
public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {
//API规范:需要用一个工厂来造解析器对象,于是我先造了一个工厂!
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
//获取解析器对象
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
//获取到解析XML文档的流对象
InputStream inputStream = DomParse.class.getClassLoader().getResourceAsStream("city.xml");
//解析XML文档,得到了代表XML文档的Document对象!
Document document = documentBuilder.parse(inputStream);
}
}
- 解析XML文档的内容用来干嘛?无非就是增删改查遍历,只要我们会对XML进行增删改查,那就说明我们是会使用DOM解析的!
遍历
- 我们再来看一下XML文档的内容,如果我们要遍历该怎么做?:
可能我们会有两种想法:
- ①:从XML文档内容的上往下看,看到什么就输出什么!【这正是SAX解析的做法】
- ②:把XML文档的内容分成两部分,一部分是有子节点的,一部分是没有子节点的(也就是元素节点!)。首先我们判断是否为元素节点,如果是元素节点就输出,不是元素节点就获取到子节点的集合,再判断子节点集合中的是否是元素节点,如果是元素节点就输出,如果不是元素节点获取到该子节点的集合….好的,一不小心就递归了…
我们来对XML文档遍历一下吧,为了更好地重用,就将它写成一个方法吧(也是能够更好地用递归实现功能)!
public class DomParse {
public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {
//API规范:需要用一个工厂来造解析器对象,于是我先造了一个工厂!
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
//获取解析器对象
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
//获取到解析XML文档的File对象
InputStream inputStream = DomParse.class.getClassLoader().getResourceAsStream("city.xml");
//解析XML文档,得到了代表XML文档的Document对象!
Document document = documentBuilder.parse(inputStream);
//把代表XML文档的document对象传递进去给list方法
list(document);
}
//我们这里就接收Node类型的实例对象吧!多态!!!
private static void list(Node node) {
//判断是否是元素节点,如果是元素节点就直接输出
if (node.getNodeType() == Node.ELEMENT_NODE) {
System.out.println(node.getNodeName());
}
//....如果没有进入if语句,下面的肯定就不是元素节点了,所以获取到子节点集合
NodeList nodeList = node.getChildNodes();
//遍历子节点集合
for (int i = 0; i < nodeList.getLength(); i++) {
//获取到其中的一个子节点
Node child = nodeList.item(i);