上一讲:使用DOM解析XML文档 内容我们讲解了DOM解析XML的主要内容和流程,把XML文档的内容解析到一个个的Java对象中去供程序使用,利用JAXP,我们只需几行代码就能做到这一点。
JAXP(Java API for XML Parsing):用于XML解析的Java API。这是SUN公司为我们提供的处理XML的接口。
1. DOM的基本对象
1) Node对象:DOM结构中最为基本的对象
2) Document对象:代表整个XML的文档
3) NodeList对象:包含一个或者多个Node的列表
4) Element对象:代表XML文档中的标签元素
通过解析XML文档,为XML文档在逻辑上建立一个树模型,树的节点是一个个对象,通过存取这些对象就能够存取XML文档的内容。遍历这个树来解析文档内容。
5)查看JDK文档中的Node,可以发现它的子接口有Attr、Document、Element、Text等 其中Text表示文本中间的内容。
2. 在DOM解析XML中,首先,我们需要建立一个解析器工厂,以利用这个工厂来获得一个具体的解析器对象。
但是工厂在哪里呢?之前我们所讲的简单工厂模式,工厂是具体存在的,而对于DOM来说,工厂我们也要通过环境变量来设置的。下面我就来具体的剖析一下这里相关的内容。
1)建立工厂的方法[基本都是按着这种模式来进行的]
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
我们在这里使用DocumentBuilderFacotry的目的是为了创建与具体解析器无关的程序,当DocumentBuilderFactory 类的静态方法newInstance()被调用时,它根据一个系统变量来决定具体使用哪一个解析器。又因为所有的解析器都服从于JAXP所定义的接口,所以无论具体使用哪一个解析器,代码都是一样的。所以当在不同的解析器之间进行切换时,只需要更改系统变量的值,而不用更改任何代码。这就是工厂所带来的好处。
在上一讲的程序中,我们试着打印出解析器工厂和解析器的类名
/* * Step 1: 获得dom解析器工厂(工厂的作用就是用于创建具体的解析器。) */ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); System.out.println("class name: " + dbf.getClass().getName()); //Step 2: 获得具体的DOM解析器 DocumentBuilder db = dbf.newDocumentBuilder(); System.out.println("class name: " + db.getClass().getName());
[输出内容]:
class name: com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
class name: com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl[说明]:建议在学习这一部分的内容的时候可以去参考一下JDK文档中DocumentBuilder的一些说明,知道解析器工厂和解析器之间的关系。
3. 下面我们再联系一下DOM解析XML的例子。
1) 解析下面的XML文档:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <学生名册> <学生 学号="1"> <姓名>张三</姓名> <性别>男</性别> <年龄>20</年龄> </学生> <学生 学号="2"> <姓名>李四</姓名> <性别>女</性别> <年龄>19</年龄> </学生> <学生 学号="3"> <姓名>王五</姓名> <性别>男</性别> <年龄>21</年龄> </学生> </学生名册>
2) 代码如下:
编译执行结果:package com.ahuier.xml.dom; import java.io.File; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; public class DomTest3 { public static void main(String[] args) throws Exception { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.parse(new File("student.xml")); /* * 打印输出student.xml的一些版本信息、编码方式等 * 输出:1.0、UTF-8、true */ // System.out.println(doc.getXmlVersion()); // System.out.println(doc.getXmlEncoding()); // System.out.println(doc.getXmlStandalone()); /* * 我们上一个例子是获得Document对象,然后获得NodeList,这会我们换一下,获得文档的根元素节点 * 查看JDK文档中的Document的方法 * 使用 Element getDocumentElement() 方法 */ Element root = doc.getDocumentElement(); System.out.println(root.getTagName()); //打印出:学生名册 /* * 查看Element中的方法 * 使用这个方法:NodeList getChildNodes() * 它返回这个对象节点的所有孩子孩子的列表,如果这个节点没有孩子节点,则返回为空 */ NodeList list = root.getChildNodes(); //注意这边打印为7个。为什么是7个呢? 因为XML默认为空格也算是一个节点的孩子节点,可以尝试把XML中元素之间的空格去掉验证 System.out.println(list.getLength()); for(int i = 0; i < list.getLength(); i++){ //打印出这个节点的各个孩子节点的节点名称 System.out.println(list.item(i).getNodeName()); } } }
学生名册
7
#text
学生
#text
学生
#text
学生
#text[说明]:
1)这边的孩子节点为7个原因是因为在XML解析中,它把元素之间的空格也算作是一个节点了,可以尝试在上诉XML中删除部分空格验证一下,如下XML文档。
[编译执行结果]:中间去点一个元素之间的空格后,结果输出为6个<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <学生名册> <学生 学号="1"> <姓名>张三</姓名> <性别>男</性别> <年龄>20</年龄> </学生><学生 学号="2"> <姓名>李四</姓名> <性别>女</性别> <年龄>19</年龄> </学生> <学生 学号="3"> <姓名>王五</姓名> <性别>男</性别> <年龄>21</年龄> </学生> </学生名册>
2) 为什么会打印出 #text 的内容呢?请查看JDK文档中的Node,这里面有很详细的描述。
nodeName
,nodeValue
, andattributes 等等根据节点类型的不同,他们会有这样的取值,如下图所示
上诉例子中空格属于文本(Text)所以在遍历的时候就 nodeName就会出现 #text的符号了,当然还可以发现上一讲程序中为什么Element打印出来是为 null的了。