XML
XML常见应用:存储有关系的数据、软件配置文件用于描述程序模块之间的关系。
- XML语法
- 组成部分
- 书写规范
- XML约束
- DTD 约束
- DTD 分类
- DTD 语法
- Schema约束(主要)
- DTD 约束
- XML解析
- DOM & SAX
- DOM
- SAX
- JAXP & DOM4J
- JAXP
- DOM4J
- XML回写
- DOM & SAX
- XPath
XML语法
组成部分
XML语法比HTML严谨,一个XML文件分为如下几部分内容:文档声明、元素、属性、注释、CDATA区 、特殊字符、处理指令
文档申明
<!-- 注意:1.空格使用英文空格 2.顶着1行1列书写 -->
<!-- 最简单的声明语法 -->
<?xml version="1.0" ?>
<!-- encoding属性说明文档的字符编码,standalone属性说明文档是否独立 -->
<?xml version="1.0" encoding="GB2312" standalone="yes" ?>
元素
- 元素分为自闭合和有开始结束标签两部分两种,都需要闭合
- 元素可以合理嵌套不能交叉嵌套
- 格式良好的XML文档必须有且仅有一个根标签,其它标签都是这个根标签的子孙标签
- 元素中的内容不能为了美化,加空格、回车、制表符之类的符号
- 元素的命名可以包含字母、数字以及其它一些可见字符,但必须遵守下面的一些规范:
- 区分大小写
- 不能以数字或下划线”_“开头
- 不能以xml、XML、Xml等开头
- 不能包含空格
- 名称中间不能包含冒号
属性
- 一个标签可以有多个属性,每个属性都有它自己的名称和取值
- 必须是键值对
- 值必须用单引号或双引号包裹
- 属性必须出现在开始标签中
- 属性的命名遵循与元素同样的规则
注释
<!–注释–>
- XML声明之前不能有注释
- 注释不能嵌套
CDATA区
character data
在编写XML文件时,有些内容可能不想让解析引擎解析执行,而是当作原始内容处理。遇到此种情况,可以把这些内容放在CDATA区里。对于CDATA区域内的内容,XML解析程序不会处理,而是直接原封不动的输出。
<![CDATA[ ]]>
特殊字符
| 特殊字符 | 替代符号 |
|---|---|
| & | & |
| < | < |
| > | > |
| " | " |
| ' | ' |
处理指令
PI(processing instruction),
处理指令用来指挥解析引擎如何解析XML文档内容。在XML文档中可以使用xml-stylesheet指令,通知XML解析引擎,应用css文件显示xml文档内容。处理指令必须以“”作为开头,以“?>”作为结尾,XML声明语句就是最常见的一种处理指令 country.css
c1 {
font-size:200px;
color:red;
}
c2 {
font-size:100px;
color:green;
}
c3 {
font-size:10px;
color:yellow;
}
c4 {
font-size:150px;
color:blue;
}
country.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="country.css"?>
<c>
<c1>中国</c1>
<c2>美国</c2>
<c3>日本</c3>
<c4>英国</c4>
</c>
书写规范
- 必须有XML文档声明;
- 必须且仅能有一个根元素;
- XML命名分大小写,例如<a>和<A>是两个不同的元素;
- 名称中可以包含:字母、数字、下划线、连字符和原点,但不能以数字、下划线开头;
- 不能以xml开头,无论是大写还是小写都不可以,例如<xml>、<Xml>、<XML>;
- 不能包含空格、例如<ab cd>是错误的;
- 元素之间必须合理包含。
XML约束
重点掌握如何导入
DTD 约束
Document Type Definition
示例
*.dtd
<!ELEMENT students (student*) >
<!ELEMENT student (name,age,sex)>
<!ELEMENT name (#PCDATA) >
<!ELEMENT age (#PCDATA) >
<!ELEMENT sex (#PCDATA) >
<!ATTLIST student number ID #REQUIRED>
*.xml
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE students SYSTEM "students.dtd">
<students>
<student number="abc">
<name></name>
<age>apple</age>
<sex>pen</sex>
</student>
</students>
DTD 分类
- 内部DTD:
- 外部DTD:
- 本地DTD(SYSTEM):
<!DOCTYPE 根元素名称 SYSTEM "dtd文件路径"> - 网络DTD(PUBLIC):
<!DOCTYPE 根元素名称 PUBLIC "描述信息例如:版本作者语言等等" "网络上DTD路径">
- 本地DTD(SYSTEM):
DTD 语法
定义元素
<!-- 定义名为name的元素,内容为文本类型(Parsed Character Data可被解析的字符数据文本数据) -->
<!ELEMENT name(#PCDATA)>
<!-- 定义名为student元素,内容依次为name、age、sex元素(后面所接符号表示子元素出现次数:*表示出现0~n次,+表示1~n次,?表示0~1次) -->
<!ELEMENT student(name*,age+,sex?)>
<!-- 定义名为student元素,内容任意 -->
<!ELEMENT student ANY>
<!-- 定义名为student元素,不能有内容,即空元素。注意:空元素是可以有属性的 -->
<!ELEMENT student EMPTY>
定义属性
<!ATTLIST 元素名字 属性名 属性类型 设置说明>
属性类型
CDATA(文本类型):<!ATTLIST student number CDATA #REQUIRED>
Enumerated(枚举类型):<!ATTLIST student sex(male|female)"male">
ID:ID类型的属性用来标识元素的唯一性,即元素的ID属性值不能与其他元素的ID属性值相同:
<!ATTLIST student number ID #REQUIRED>
<!ATTLIST student friend IDREF #IMPLIED>
IDREF(ID引用类型):用来指定另一个元素,与另一个元素建立关联关系,IDREF类型的属性值必须是另一个元素的ID:
设置说明
#REQUIRED:说明属性是必须的;
#IMPLIED:说明属性是可选的;
Schema约束(主要)
- Schema是新的XML文档约束,DTD出现的比较早;
- Schema要比DTD强大很多,能对内容进行约束;
- Schema本身也是XML文档,但Schema文档的扩展名为xsd。
示例
*.xsd
<?xml version="1.0"?>
<xsd:schema xmlns="http://www.itcast.cn/xml"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.itcast.cn/xml" elementFormDefault="qualified">
<xsd:element name="students" type="studentsType"/>
<xsd:complexType name="studentsType">
<xsd:sequence>
<xsd:element name="student" type="studentType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="studentType">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="age" type="ageType"/>
<xsd:element name="sex" type="sexType"/>
</xsd:sequence>
<xsd:attribute name="number" type="numberType" use="required"/>
</xsd:complexType>
<xsd:simpleType name="sexType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="male"/>
<xsd:enumeration value="female"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="ageType">
<xsd:restriction base="xsd:integer">
<xsd:minInclusive value="0"/>
<xsd:maxInclusive value="120"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="numberType">
<xsd:restriction base="xsd:string">
<xsd:pattern value="ITCAST_\d{4}"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>
*.xml
<!-- 引入Schema约束:
1. 书写根元素(因为xsd文件的引入是在根元素上进行的);
2. 在根元素上书写schemaLocation属性,填入命名空间,和xsd文件位置(可以引入多个,每队之间用 空格/回车 隔开);
3. 为引入的xsd定义一个前缀:xmlns="http://www.itcast.cn/xml";
4. 固定值:xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"。
-->
<students xmlns="http://www.itcast.cn/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.itcast.cn/xml students.xsd" >
<student number="ITCAST_0001">
<name>tom</name>
<age>18</age>
<sex>male</sex>
</student>
</students>
XML解析
系统从xml中读取数据的过程
DOM & SAX
Document Object Model & Simple API for XML: 两个常见的解析xml的思想
DOM
DOM 是基于树形结构的XML解析方式,它会将整个XML文档读入内存并构建一个DOM树,基于这棵树形结构对于各个节点(Node)进行操作。XML文档中的每一个成分都是一个节点。一个XML文档解析后对应一个Document对象。
- 优点:元素与元素之间的结构关系保留了下来,易于进行增删改查
- 缺点:如果XML文档数据量较大,那么会造成较大的资源消耗
DOM把所有内容封装成了五类对象:Document、Element、Attribute、Text、Commons,它们共同的父类是 Node
Node
- 自身属性:nodeType、nodeName、nodeValue
- 导航属性:
寻找子节点的:firstChild, lastChild, childNodes
寻找父节点的:parentNode
寻找兄弟节点的:nextSibling, previousSibling
获取Element的方式:
getElementById // Document
getElementsByTagName // Document/Element
getElementsByClassName // Document/Element
getElementsByName // Document
增删改查:
document.createElement // 创建一个元素
element.appendChild
element.insertBefore // 添加一个元素
element.replaceChild // 替换一个元素
element.removeChild // 删除一个元素
DOM示例
<?xml version="1.0" encoding="utf-8" ?>
<students>
<student number="0001">
<name>Wang</name>
<age>19</age>
<sex>male</sex>
</student>
<student number="002">
<name>Gu</name>
<age>18</age>
<sex>female</sex>
</student>
</students>
package DOM;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
// 使用DOM解析获得所有学生信息
public class DOM {
public static void main(String[] args) {
// 1.获得jaxp工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
// 2.通过工厂获得解析器实现类
DocumentBuilder builder = factory.newDocumentBuilder();
// 3.使用解析器加载xml文档
Document doc = builder.parse(new File("src/DOM/students.xml"));
// 4.获得所有学生元素的集合
NodeList studentList = doc.getElementsByTagName("student");
// 5.遍历集合
for (int i = 0; i < studentList.getLength(); i++) {
Element stuEle = (Element) studentList.item(i);
// 获得学生元素的number属性
String number = stuEle.getAttribute("number");
System.out.println("学号:" + number);
// 获得学生节点下的所有子节点
NodeList children = stuEle.getChildNodes();
// 遍历集合并提取name, age, sex元素对象
for (int j = 0; j < children.getLength(); j++) {
Node node = children.item(j);
// 方式1: if(node instanceof Element){ }
// 方式2:
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element child = (Element) node;
if (child.getNodeName().equals("name")) {
String name = child.getTextContent();
System.out.println("姓名:" + name);
} else if (child.getNodeName().equals("age")) {
String age = child.getTextContent();
System.out.println("年龄:" + age);
} else if (child.getNodeName().equals("sex")) {
String sex = child.getTextContent();
System.out.println("性别:" + sex);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
SAX
SAX 是基于事件模型的XML解析方式,它并不需要将整个XML文档加载到内存中,而只需将XML文档的一部分加载到内存中,即可开始解析,在处理过程中并不会在内存中记录XML中的数据,所以占用的资源比较少。 SAX解析将XML档的读取过程,划分出5类事件(文档开始/结束事件、元素开始/结束事件、文本事件),根据自己感兴趣的事件注册相应的回调函数。只需继承SAX提供的DefaultHandler基类,重写相应的事件处理方法并进行注册即可。事件是由解析器产生并通过回调函数发送给应用程序的,这种模式称作为“推模式”(Pull)。
- 优点:因为文件内存占用小,所以适合解析大的XML
- 缺点:因为不储存XML文档的结构,所以需要自己负责维护业务逻辑涉及的多层节点之间的关系。当XML文档非常复杂时,维护节点间关系的复杂度较高,工作量就会比较大。另一方面,因为是流式处理,所以处理过程只能从XML文档开始向后单项进行,无法像DOM方式那样自由导航到之前处理过的节点上重新处理,也无法支持XPath。SAX没有提供写XML文档的功能。
示例
package SAX;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
// 使用SAX解析获取某个学生信息
public class SAXHandler extends DefaultHandler {
// 学生学号
private String number;
public SAXHandler(String number) {
super();
this.number = number;
}
private boolean flag = false;
private String current = "";
@Override
public void startDocument() throws SAXException {
}
@Override
//参数3:告知开发者当前触发的元素 参数4:提供标签上的属性
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
current = qName;
if (qName.equals("student") && attributes.getValue("number").equals(number)) {
flag = true;
}
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
String str = new String(ch, start, length);
//去掉两端的空白字符 回车 空格 制表符
str = str.trim();
if (flag && str.length() > 0) {
if (current.equals("name")) {
System.out.println("姓名:" + str);
} else if (current.equals("age")) {
System.out.println("年龄:" + str);
} else if (current.equals("sex")) {
System.out.println("性别:" + str);
}
}
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
if (qName.equals("student")) {
flag = false;
}
}
@Override
public void endDocument() throws SAXException {
}
}
package SAX;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
public class SAX {
public static void main(String[] args) {
// 1.获得解析器工厂类
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
// 2.获得解析器
SAXParser parser = factory.newSAXParser();
// 3.解析文档
parser.parse(new File("src/SAX/students.xml"), new SAXHandler("0001"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*output:
* 姓名:Wang
* 年龄:19
* 性别:male
* */
JAXP & DOM4J
JAXP
java api for xml parser
JAXP是JDK提供的一套用于解析XML的API,它很好地支持DOM和SAX解析方式。
DOM4J
Dom for java
整合SAX和DOM两种思想,使用sax的思想做读取xml,又参照dom的思想,也在内存中创建了一颗对象关系树(优化后属于自己的新树,与dom树不是同一个树,有细节变动,但不影响,可参照之前我们学习的dom树)
CURD示例
package dom4j;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import java.io.File;
import java.io.FileOutputStream;
// 添加一个学生元素: jack 0003 19 male
public class Create {
public static void main(String[] args) {
SAXReader reader = new SAXReader();
try {
Document document = reader.read(new File("src/dom4j/students.xml"));
Element root = document.getRootElement();
// 1.添加student元素及其number属性
Element studentEle = root.addElement("student").addAttribute("number", "0003");
// 2.添加name、age、sex子元素并添加子元素中的文本
studentEle.addElement("name").addText("jack");
studentEle.addElement("age").addText("19");
studentEle.addElement("sex").addText("male");
// 3.将document对象写到文件中
// 创建格式化器
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("UTF-8");
// 创建写入器
// XMLWriter writer = new XMLWriter(new FileWriter("src/students_copy.xml"),format);
// 使用字节流避免出现乱码
XMLWriter writer = new XMLWriter(new FileOutputStream("src/dom4j/students_add.xml"), format);
// 写入
writer.write(document);
// 关闭资源
writer.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package dom4j;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import java.io.File;
import java.io.FileOutputStream;
// 删除学生0001
public class Delete {
public static void main(String[] args) {
SAXReader reader = new SAXReader();
try {
Document document = reader.read(new File("src/dom4j/students.xml"));
Element root = document.getRootElement();
// 使用XPath语言查询XML文档而非遍历,可提高效率
String xpath = "//student[@number='0001']";
Element student = (Element) document.selectSingleNode(xpath);
// 删除:先获取父节点再删除该节点
System.out.println(student.getParent().remove(student));
// 回写
XMLWriter writer = new XMLWriter(new FileOutputStream("src/dom4j/students_delete.xml"), OutputFormat.createPrettyPrint());
writer.write(document);
writer.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package dom4j;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import java.io.File;
import java.io.FileOutputStream;
// 将学号为0001号的学生信息修改为:rose 16 female
public class Update {
public static void main(String[] args) {
SAXReader reader = new SAXReader();
try {
Document document = reader.read(new File("src/students.xml"));
String xpath = "//student[@number='itcast_0001']";
Element studentEle = (Element) document.selectSingleNode(xpath);
studentEle.element("name").setText("rose");
studentEle.element("age").setText("16");
studentEle.element("sex").setText("female");
XMLWriter writer = new XMLWriter(new FileOutputStream("src/students_edit.xml"), OutputFormat.createPrettyPrint());
writer.write(document);
writer.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package dom4j;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.util.List;
// 查询出所有学生的所有信息
public class Retrieve {
public static void main(String[] args) {
try {
SAXReader reader = new SAXReader();
Document document = reader.read(new File("src/students.xml"));
// 1.获得文档根节点
Element root = document.getRootElement();
// 2.迭代根元素下的所有名叫student的子元素
/* 方式一:
for (Iterator<Element> it = root.elementIterator("student"); it.hasNext(); ) {
Element student = it.next();
// ...
}
*/
// 方式二:
List<Element> list = root.elements("student");
for (Element student : list) {
//3.获得student元素的number属性
String number = student.attributeValue("number");
//4.student子元素的内容(name age sex)
String name = student.elementText("name");
String age = student.elementText("age");
String sex = student.elementText("sex");
System.out.println("学号:" + number + ",姓名:" + name + ",年龄:" + age + ",性别:" + sex + "");
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
}
节点相关操作
// 获取文档的根节点
SAXReader reader = new SAXReader();
Document document = reader.read(new File("src/students.xml"));
Element root=document.getRootElement();
// 获取某个节点的子节点
Element element=node.element("名称");
// 获得节点的文字
String text=node.getText();
// 获得某节点下所有名为“member”的子节点,并进行遍历
List nodes=rootElm.elements("member");
for(Iterator it=nodes.iterator;it.hasNext();){
Element elm=(Element)it.next();
//do something
}
// 对某节点的所有子节点进行遍历
for(Iterator it=root.elementIterator();it.hasNext();){
Element elm=(Element)it.next();
//do something
}
// 对某节点下添加子节点
Element ageElm=newMemberElm.addElement("age");
// 设置节点文字
element.setText("29");
// 删除某节点
parentElm.remove(childElm); // childElm是待删除的节点,parentElm是其父节点
// 添加一个CDATA节点
Element contentElm=infoElm.addElement("content");
contentElm.addCDATA(diary.getContent);
......
属性相关操作:
// 获取某节点下的某属性
Element root=document.getRootElement();
// 属性名name
Attribute attribute=root.attribute("size");
// 获取属性的文字
String text=attribute.getText();
// 删除某属性
Attribute attribute=root.attribute("size");
root.remove(attribute);
// 遍历某节点的所有属性
Element root=document.getRootElement();
for(Iterator it=root.attributeIterator;it.next();){
String text=(attribute)it.next();
System.out.println(text);
}
//设置某节点的属性和文字
newMemberElm.addAttribute("name","sitinspring");
//设置属性的文字
Attribute attribute=root.attribute("name");
attribute.setText("sitinspring" );
......
XML回写
1.文档中全为英文,不设置编码,直接写入的形式
XMLWriter writer=new XMLWriter(new FileWriter("output.xml"));
writer.writer(document);
writer.close();
2.文档中含有中文,设置编码格式如下:
OutputFormat format=OutputFormat.creatPrettyPrint();
format.setEncoding("GBK");//指定XML编码
XMLWriter writer=new XMLWriter(new FileWriter("output.xml"),format);
writer.writer(document);
writer.close();
Q:使用dom4j如何避免乱码? 保证写入的编码和读取的编码一致.
-
写入的编码如何控制?
XMLWriter writer = new XMLWriter(new PrintWriter("src/students_copy.xml","UTF-8"),format); -
读取的编码如何控制?
<?xml version="1.0" encoding="GBK"?>
其中encoding属性决定了读取时采用什么编码,而encoding属性由
format.setEncoding("GBK");控制.
以上方法太过繁琐,推荐使用使用字节流就不会出现乱码
XMLWriter writer = new XMLWriter(new FileOutputStream("src/students_copy.xml"),format);
XPath
XPath之于XML就好比SQL语言之于数据库
XPath是一种为查询XML文档而设计的语言,它可以与DOM解析方式配合使用,实现对XML文档的解析。XPath使用路径表达式选取XML文档中指定的节点或者节点集合,与常见的URL路径类似。
XPath中常用表达式
| 表达式 | 含义 |
|---|---|
| nodename | 选取指定节点的所有节点 |
| / | 从根节点选取指定节点 |
| // | 根据指定表达式,在整个文档中选取匹配的节点,不考虑匹配节点在文档中的位置 |
| . | 选取当前节点 |
| .. | 选取当前节点的父节点 |
| @ | 选取属性 |
| * | 匹配任何元素节点 |
| @* | 匹配任何属性节点 |
| node() | 匹配任何类型的节点 |
| text() | 匹配文本节点 |
| 竖线 | 选取若干路径 |
| [ ] | 指定某个节点,用于查找某个特定节点或包含某个指定值的节点 |
示例: 解析inventory.xml文档,查找作者为Neal Stephenson所有书籍的标题
<!-- inventory.xml -->
<inventory>
<book year="2000">
<title>Snow Crash</title>
<author>Neal Stephenson</author>
<publisher>Spectra</publisher>
<isbn>0553380958</isbn>
<price>14.95</price>
</book>
<book year="2005">
<title>Burning Tower</title>
<author>Larry Niven</author>
<publisher>Jerry Pournelle</publisher>
<isbn>0743416910</isbn>
<price>5.99</price>
</book>
<book year="1995">
<title>Zodiac</title>
<author>Neal Stephenson</author>
<publisher>Spectra</publisher>
<isbn>0553573862</isbn>
<price>7.50</price>
</book>
<!-- more books...-->
</inventory>
package xpath;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.*;
import java.io.IOException;
public class XPathTest {
public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException, XPathExpressionException {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
// 开启验证
documentBuilderFactory.setValidating(true);
documentBuilderFactory.setNamespaceAware(false);
documentBuilderFactory.setIgnoringComments(true);
documentBuilderFactory.setIgnoringElementContentWhitespace(false);
documentBuilderFactory.setCoalescing(false);
documentBuilderFactory.setExpandEntityReferences(true);
// 创建DocumentBuilder
DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
// 设置异常处理对象
builder.setErrorHandler(new ErrorHandler() {
@Override
public void warning(SAXParseException exception) throws SAXException {
System.out.println("WARN:" + exception.getMessage());
}
@Override
public void error(SAXParseException exception) throws SAXException {
System.out.println("fatalError:" + exception.getMessage());
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
System.out.println("error:" + exception.getMessage());
}
});
// 将文档加载到一个Document对象中
Document doc = builder.parse("src/xpath/inventory.xml");
// 创建 XPathFactory
XPathFactory factory = XPathFactory.newInstance();
// 创建 XPath 对象
XPath xpath = factory.newXPath();
// 编译 XPath 表达式,查询作者为Neal Stephenson的图书的标题
XPathExpression expr = xpath.compile("//book[author='Neal Stephenson']/title/text()");
/*
* 执行XPath表达式得到结果
* 第一个参数指定了XPath表达式进行查询的上下文节点,本例查询整个文档
* 第二个参数指定XPath表达式的返回类型,XPathConstants类提供了nodeset、boolean、number、string 和 Node 五种类型
* */
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result; //强制类型转换
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i).getNodeValue());
}
// 查询1997年之后的图书的标题
/*
* 如果XPath表达式只使用一次,可以跳过编译步骤直接调用XPath对象的evaluate()方法进行查询。
* 如果需要重复使用多次,先进行编译在查询,可提高性能
* */
nodes = (NodeList) xpath.evaluate("//book[@year>1997]/title/text()", doc, XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i).getNodeValue());
}
// 查询1997年之后的图书的属性和标题
nodes = (NodeList) xpath.evaluate("//book[@year>1997]/@*|//book[@year>1997]/title/text()", doc, XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i).getNodeValue());
}
}
}