XML的概念与用途
XML的概念
- XML的全称是Extensible Markup Language 即:可扩展标记语言
- 编写XML就是编写标签,与HTML非常类似,扩展名为.xml
类似格式如下:hr.xml
<employee> <name>张三</name> <age>31</age> <height>178</height> </employee>
- 良好的人机可读性
XML的用途
- Java程序的配置描述文件
- 用于保存程序产生的数据
- 用于网络间的数据传输
XML的文档结构
- 第一行必须是XML声明
XML声明说明XML文档的基本信息,包括版本号与字符集,写在XML第一行
version代表版本号 1.0/1.1<?xml version = "1.0" encoding = "UTF-8"?>
encoding = "UTF-8" 为设置字符集 - 有且只有一个根节点
- XML标签的书写规则与HTML相同
示例:hr.xml
<?xml version = "1.0" encoding = "UTF-8"?> <!--人力资源管理系统--> <hr> <employee no="27149"> <name>张三</name> <age>31</age> <salary>4000</salary> <department> <dname>会计部</dname> <address>没有科技-103</address> </department> </employee> <employee no="27150"> <name>李四</name> <age>29</age> <salary>10000</salary> <department> <dname>研发部</dname> <address>没有科技-104</address> </department> </employee> </hr>
XML标签书写规则
合法的标签名
- 标签名要有意义
- 建议使用英文,小写字母,单词之间使用"-"分割
- 多级标签之间不要重名
正确的标签格式示例:
<shop-cart>
<item>相册</item>
</shop-cart>
适当的注释与缩进
适当的注释与缩进会增强阅读性
合理使用属性
- 标签的属性用于描述标签不可或缺的信息
- 对标签分组或者为标签设置id时常用属性表示
特殊字符与CDATA标签的使用
- 标签体中,出现"<",">"等特殊字符,会破坏文档的结构
如:下面的XML是无效的,不合法的<exam> <question> 1 + 4 < 3 是否正确?</question> <question> 3 + 5 > 8 是否正确?</question> </exam>
- 解决特殊字符破坏文档的方法一:使用实体引用
XML支持五种实体引用-
<
对应<
-
>
对应>
-
&
对应&
-
'
对应'
-
"
对应"
-
- 解决特殊字符破坏文档的方法二:使用CDATA标签
CDATA标签指的是不应该由XML解析器进行解析的文本数据
使用方法:从<![CDATA[开始,到]]>
结束
有序的子元素
有序的子元素是指:在XML多层嵌套的子元素中,标签前后顺序应该保持一致
XML语义约束
XML语义约束就是用于规定XML文档中允许出现哪些元素
XML语义约束有两种定义方式:DTD 与 XML Schema
XML语义约束之DTD
DTD,即:Document Type Definition,翻译过来就是文档定义类型。DTD是一种简单易用的语句约束方式。
DTD文件的扩展名为.dtd
- 利用DTD中的
<!ELEMENT>
标签,我们可以定义XML文档中允许出现的节点及数量
以hr.xml 作为示例说明:定义hr节点下只允许出现1个employee子节点 <!ELEMENT hr(employee)> 定义hr节点下最少出现1个employee子节点 <!ELEMENT hr(employee+)> 定义hr节点下可出现 0...n个employee子节点 <!ELEMENT hr(employee*)> 定义hr节点下最多出现1个employee子节点 <!ELEMENT hr(employee?)> 定义employee节点下必须包含以下四个节点,并且需要按照顺序出现 <!ELEMENT employee(name,age,salary,department)> 定义name标签只能是文本,#PCDATA代表文本元素 <!ELEMENT name(#PCDATA)>
- 在XML中使用
<!DOCTYPE>
标签来引用DTD文件书写格式: <!DOCTYPE 根节点 SYSTEM "dtd文件路径"> 示例: <!DOCTYPE hr SYSTEM "hr.dtd">
DTD语义约束示例
拿hr.xml文件进行举例,首先在同目录下创建hr.dtd文件,并在hr.xml文件中引入hr.dtd
<?xml version = "1.0" encoding = "UTF-8"?>
<!--人力资源管理-->
<!DOCTYPE hr SYSTEM "hr.dtd">
<hr>
<employee no="27149">
<name>张三</name>
<age>31</age>
<salary>4000</salary>
<department>
<dname>会计部</dname>
<address>没有科技-103</address>
</department>
</employee>
<employee no="27150">
<name>李四</name>
<age>29</age>
<salary>10000</salary>
<department>
<dname>研发部</dname>
<address>没有科技-104</address>
</department>
</employee>
</hr>
hr.dtd文件如下:
<?xml version = "1.0" encoding = "UTF-8"?>
<!ELEMENT hr (employee+)>
<!ELEMENT employee (name,age,salary,department)>
<!-- employee 中的 no 属性 属于 CDATA 即:不应该被XML解析的部分 默认值为 "" -->
<!ATTLIST employee no CDATA "">
<!-- #PCDATA 为 纯文本 -->
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
<!ELEMENT salary (#PCDATA)>
<!ELEMENT department (dname,address)>
<!ELEMENT dname (#PCDATA)>
<!ELEMENT address (#PCDATA)>
XML Schema
- XML Schema 比DTD更为复杂,并且提供了更多的功能
- XML Schema 提供了数据类型,格式限定,数据范围等特性
- XML Schema 是W3C标准
以hr.xml举例,对应的xsd文件为:
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="hr">
<!--complexType标签的含义是复杂节点,包含子节点时必须使用这个标签-->
<xs:complexType>
<xs:sequence>
<xs:element name="employee" minOccurs="1" maxOccurs="9999">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"></xs:element>
<xs:element name="age">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:minInclusive value="18"></xs:minInclusive>
<xs:maxInclusive value="60"></xs:maxInclusive>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="salary" type="xs:integer"></xs:element>
<xs:element name="department">
<xs:complexType>
<xs:sequence>
<xs:element name="dname" type="xs:string"></xs:element>
<xs:element name="address" type="xs:string"></xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<!--required 必须的;表明 no 这个属性在任何employee节点下必须存在-->
<xs:attribute name="no" type="xs:string" use="required"></xs:attribute>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
在hr.xml文件中引入这个xmd文件需要在根节点上加入:
<?xml version = "1.0" encoding = "UTF-8"?>
<!--人力资源管理-->
<!--<!DOCTYPE hr SYSTEM "hr.dtd">-->
<hr xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="hr.xsd">
<employee no="27149">
<name>张三</name>
<age>31</age>
<salary>4000</salary>
<department>
<dname>会计部</dname>
<address>没有科技-103</address>
</department>
</employee>
<employee no="27150">
<name>李四</name>
<age>29</age>
<salary>10000</salary>
<department>
<dname>研发部</dname>
<address>没有科技-104</address>
</department>
</employee>
</hr>
XML文档解析及XPath语言
DOM文档模型与Dom4j
- DOM(Document Object Model) 定义了访问和操作XML文档的标准方法,DOM把XML文档作为树结构来查看,能够通过DOM树来读写所有元素。
- Dom4j是一个易用的,开源的库,用于解析XML。它应用于Java平台,具有性能优异,功能强大和极易使用的特点。
- Dom4j将XML视为Document对象
- XML标签被Dom4j定义为Element对象
Dom4j读取遍历XML
继续使用hr.xml文件作为示例,首先需要引入dom4j的jar包,在这里就不赘述了,直接看代码即可:
package dom4j;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.util.List;
public class HrReader {
public static void readXml(){
String file = "C:/Users/11750/IdeaProjects/xml/src/hr.xml";
// SAXReader类是读取XML文件的核心类,用于将XML解析后以树的形式保存在内存中
SAXReader reader = new SAXReader();
try {
Document document = reader.read(file);
// 获取XML文档的根节点
Element root = document.getRootElement();
List<Element> employees = root.elements("employee");
for(Element employee : employees){
// 获取属性
Attribute no = employee.attribute("no");
System.out.print(no.getText() + " ");
// element方法用于获取唯一的子节点对象
Element name = employee.element("name");
String empName = name.getText(); // getText方法用于获取标签文本
System.out.print(empName + " ");
// or
System.out.print(employee.elementText("age") + " ");
System.out.print(employee.elementText("salary") + " ");
//
Element department = employee.element("department");
System.out.print(department.elementText("dname") + " ");
System.out.println(department.elementText("address") + " ");
System.out.println();
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
HrReader.readXml();
}
}
显示结果如下:
Dom4j 更新XML
Dom4j除了可以读取XML文档中数据,还可以向XML文档中写入数据,继续使用hr.xml文件作为示例,向hr.xml写入新的员工:
package dom4j;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
public class HrWriter {
public static void writeXml(){
String file = "C:/Users/11750/IdeaProjects/xml/src/hr.xml";
SAXReader reader = new SAXReader();
try {
Document document = reader.read(file);
Element root = document.getRootElement();
Element newEmp = root.addElement("employee");
// add attribute
newEmp.addAttribute("no","3311");
// set name
Element name = newEmp.addElement("name");
name.setText("王五");
// set age
newEmp.addElement("age").setText("26");
// set salary
newEmp.addElement("salary").setText("5200");
// set
Element department = newEmp.addElement("department");
department.addElement("dname").setText("人事部");
department.addElement("address").setText("没有科技-105");
// 输出流写入到 xml 文件中
Writer writer = new OutputStreamWriter(new FileOutputStream(file),"UTF-8");
document.write(writer);
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
HrWriter.writeXml();
}
}
更新XML文档后,发现已经添加了王五这名原工的信息,再次使用HrReader.readXml()
遍历所有员工可以得到:
XPath路径表达式
- XPath 路径表达式是XML文档中查找数据的语音
- 掌握XPath可以极大的提高在提取数据时的开发效率
- 学习XPath本质就是掌握各种形式的表达式的使用技巧
几种最常用的基本表达式有:
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点 |
/ | 从根节点选取 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置 |
. | 选取当前节点 |
.. | 选取当前节点的父节点 |
@ | 选取属性 |
XPath基本表达式案例
路径表达式 | 结果 |
---|---|
bookstore | 选取bookstore元素的所有子节点 |
/bookstore | 选取根元素bookstore |
bookstore/book | 选取属于 bookstore 的子元素的所有book元素 |
//book | 选取所有book子元素,而不管它们在文档中的位置 |
bookstore//book | 选取bookstore元素的后代的所有book元素,而不管他们位于bookstore下面的什么位置 |
//@lang | 选取名为lang的所有属性 |
XPath 谓语表达式
路径表达式 | 结果 |
---|---|
/bookstore/book[1] | 选取bookstore子元素的第一个book元素 |
/bookstore/book[last()] | 选取属于bookstore子元素的最后一个 book元素 |
/bookstore/book[last() - 1] | 选取属于bookstore子元素的倒数第二个 book元素 |
/bookstore/book[position() < 3] | 选取最前面的两个属于bookstore元素的子元素的book元素 |
//title[@lang] | 选取所有拥有名为lang的属性的title元素 |
//title[@lang='eng'] | 选取所有title元素,且这些元素拥有值为eng的lang属性 |
/bookstore/book[price > 35.00] | 选取bookstore 元素的所有book元素,且其中price的值要大于35.00 |
/bookstore/book[price > 35.00]/title | 选取bookstore 元素的所有book元素下的title元素,且其中price的值要大于35.00 |
Jaxen与XPath实战
Jaxen
- Jaxen是一个Java编写的开源的XPath库,可以适应多种不同的对象模型,包括DOM,XOM,dom4j和JDOM
- Dom4j底层依赖Jaxen实现XPath查询
- Jaxen下载地址:jaxen.codehaus.org
Dom4j里面提供了用来支持XPath的方法,不过使用XPath表达式之前,还需要引入支持XPath的jar包。
示例如下:
hr.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<!--人力资源管理--><!--<!DOCTYPE hr SYSTEM "hr.dtd">-->
<hr xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="hr.xsd">
<employee no="3301">
<name>李铁柱</name>
<age>37</age>
<salary>3600</salary>
<department>
<dname>人事部</dname>
<address>XX大厦-B105</address>
</department>
</employee>
<employee no="3302">
<name>林海</name>
<age>50</age>
<salary>7000</salary>
<department>
<dname>财务部</dname>
<address>XX大厦-B106</address>
</department>
</employee>
<employee no="3303">
<name>安娜</name>
<age>24</age>
<salary>4600</salary>
<department>
<dname>人事部</dname>
<address>XX大厦-B105</address>
</department>
</employee>
<employee no="3304">
<name>张晓宇</name>
<age>29</age>
<salary>3000</salary>
<department>
<dname>后勤部</dname>
<address>XX大厦-B108</address>
</department>
</employee>
<employee no="3305">
<name>赵子轩</name>
<age>19</age>
<salary>1500</salary>
<department>
<dname>后勤部</dname>
<address>XX大厦-B108</address>
</department>
</employee>
<employee no="3306">
<name>张晓璇</name>
<age>20</age>
<salary>1700</salary>
<department>
<dname>后勤部</dname>
<address>XX大厦-B108</address>
</department>
</employee>
<employee no="3307">
<name>张檬</name>
<age>43</age>
<salary>8700</salary>
<department>
<dname>会计部</dname>
<address>XX大厦-B103</address>
</department>
</employee>
<employee no="3308">
<name>李梅</name>
<age>33</age>
<salary>8700</salary>
<department>
<dname>工程部</dname>
<address>XX大厦-B104</address>
</department>
</employee>
<employee no="3309">
<name>张三</name>
<age>31</age>
<salary>4000</salary>
<department>
<dname>会计部</dname>
<address>XX大厦-B103</address>
</department>
</employee>
<employee no="3310">
<name>李四</name>
<age>23</age>
<salary>3000</salary>
<department>
<dname>工程部</dname>
<address>XX大厦-B104</address>
</department>
</employee>
</hr>
引入Jaxen的jar包:jaxen-1.1.6.jar后,对XPath的测试程序如下:
package dom4j;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import java.util.List;
public class XPathTest {
public static void xPath(String xPathExp){
String file = "C:/Users/11750/IdeaProjects/xml/src/hr.xml";
SAXReader reader = new SAXReader();
try {
Document document = reader.read(file);
// 执行 XPath表达式:document.selectNodes(xPathExp)
// Node 为 Element和 Attribute 的父类
List<Node> nodes = document.selectNodes(xPathExp);
for(Node node : nodes){
Element emp = (Element) node;
System.out.println("编号: " + emp.attributeValue("no"));
System.out.println("姓名: " + emp.elementText("name"));
System.out.println("年龄: " + emp.elementText("age"));
System.out.println("薪水: " + emp.elementText("salary"));
System.out.println("===============");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// 获取到所有的employee节点
XPathTest.xPath("/hr/employee");
XPathTest.xPath("//employee");
// 统计所有工资小于4000的员工
XPathTest.xPath("//employee[salary<4000]");
// 查询李铁柱这名员工的信息
XPathTest.xPath("//employee[name='李铁柱']");
// 查询编号为3304的这名员工的信息
XPathTest.xPath("//employee[@no=3304]");
// 查询第一个员工的信息
XPathTest.xPath("//employee[1]");
// 查询最后一个员工的信息
XPathTest.xPath("//employee[last()]");
// 查询前五个员工的信息
XPathTest.xPath("//employee[position()<6]");
// 查询第三名员工和第八名员工的信息
XPathTest.xPath("//employee[3] | //employee[8]");
}
}