一、概述
概念:Extensible Markup Language 可扩展标记语言 * 可扩展:标签都是自定义的。<user> <student>功能: 存储数据、配置文件、在网络中传输
1.1 语法
- xml文档的后缀名 .xml
- xml第一行必须定义为文档声明
- xml文档中有且仅有一个根标签
- 属性值必须使用引号(单双都可)引起来
- 标签必须正确关闭
- xml标签名称区分大小写
- 默认使用UTF-8编码
1.2 结构概述
<!– 注释内容 -->
<?xml version='1.0' ?>
<users>
<user id='1'>
<name>zhangsan</name>
<age>23</age>
<gender>male</gender>
<br/>
</user>
<user id='2'>
<name>lisi</name>
<age>24</age>
<gender>female</gender>
</user>
</users>
<!–- 可包含特殊字符 -->
<![CDATA[ 数据 ]]><?xml version='1.0' ?>:文档声明 格式:<?xml 属性列表 ?>
- 属性列表:
- version:版本号,必须的属性
- encoding:编码方式。告知解析引擎当前文档使用的字符集,默认值:ISO-8859-1
- standalone:是否独立
- 取值:
- yes:不依赖其他文件
- no:依赖其他文件
- 取值:
1.3 约束
规定xml文档的书写规则。 分类:
- DTD:一种简单的约束技术 引入dtd文档到xml文档中 可以约束XML文件的编写。不能约束具体的数据类型。 内部dtd:将约束规则定义在xml文档中 外部dtd:将约束的规则定义在外部的dtd文件中 本地: 网络:
- Schema:一种复杂的约束技术 填写xml文档的根元素 可以约束XML文件的标签内容格式,以及具体的数据类型。本身也是xml文件,格式更严谨。 引入xsi前缀. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 引入xsd文件命名空间. xsi:schemaLocation="http://www.itcast.cn/xml student.xsd" 为每一个xsd约束声明一个前缀,作为标识 xmlns="http://www.itcast.cn/xml"
1.4 解析
解析:操作xml文档,将文档中的数据读取到内存中 写入:将内存中的数据保存到xml文档中。持久化的存储
解析XML的方式:
- DOM:将标记语言文档一次性加载进内存,在内存中形成一颗dom树
- 优点:操作方便,可以对文档进行CRUD的所有操作
- 缺点:占内存
- SAX:逐行读取,基于事件驱动的。
- 优点:不占内存。
- 缺点:只能读取,不能增删改
二、解析器
常见的解析器:
- JAXP:sun公司提供的解析器,支持dom和sax两种思想
- DOM4J:一款非常优秀的解析器
- Jsoup:jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。
- PULL:Android操作系统内置的解析器,sax方式的。
2.1 jsoup
jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。
解析实现
//2.1获取student.xml的path
String path = JsoupDemo1.class.getClassLoader().getResource("student.xml").getPath();
//2.2解析xml文档,加载文档进内存,获取dom树--->Document
Document document = Jsoup.parse(new File(path), "utf-8");
//3.获取元素对象 Element
Elements elements = document.getElementsByTag("name");
System.out.println(elements.size());
//3.1获取第一个name的Element对象
Element element = elements.get(0);
//3.2获取数据
String name = element.text();
System.out.println(name);类说明
Jsoup:工具类,可以解析html或xml文档,返回Document
parse(File in, String charsetName)//解析xml或html文件的。
parse(String html)//解析xml或html字符串
parse(URL url, int timeoutMillis)//通过网络路径获取指定的html或xml的文档对象Document:文档对象。代表内存中的dom树
getElementById(String id)//根据id属性值获取唯一的element对象
getElementsByTag(String tagName)//根据标签名称获取元素对象集合
getElementsByAttribute(String key)//根据属性名称获取元素对象集合
getElementsByAttributeValue(String key, String value)//根据对应的属性名和属性值获取元素对象集合select 使用选择器语法查找元素 tagname: 通过标签查找元素,比如:a ns|tag: 通过标签在命名空间查找元素,比如:可以用 fb|name 语法来查找<fb:name>元素 #id: 通过ID查找元素,比如:#logo .class: 通过class名称查找元素,比如:.masthead [attribute]: 利用属性查找元素,比如:[href][attr=value]: 利用属性值来查找元素,比如:[width=500] el#id: 元素+ID,比如: div#logo el.class: 元素+class,比如: div.masthead el[attr]: 元素+属性名,比如: a[href] 任意组合,比如:a[href].highlight ancestor child: 查找某个元素下子元素,比如:.body p 查找"body"下的所有 p parent > child: 查找某个父元素下的直接子元素,比如:div.content > p 查找 p parent > * 查找某个父元素下所有直接子元素
// 组合选择器
// el#id: 元素+ID,比如: div#logo
str = doc.select("li#auto-header-fenzhan").first().text();
// el.class: 元素+class,比如: div.masthead
str = doc.select("a.greylink").first().text();
// el[attr]: 元素+属性,比如: a[href]
str = doc.select("a[href]").first().attr("href");
// 任意组合,比如:a[href].highlight
str = doc.select("a[href].greylink").first().attr("href");
// ancestor child: 查找某个元素下子元素,比如:可以用.body p 查找"body"下的所有 p
str = doc.select("div.mini-left a").text();
// parent > child: 查找某个父元素下的直接子元素,比如:div.content > p 查找 p
str = doc.select("div.mini-left ul > li").text();
// parent > * 查找某个父元素下所有直接子元素
Elements elements = doc.select("div.mini-left > *");
for (Element ele : elements) {
System.out.println(ele.tagName());
}Elements:元素Element对象的集合。可以当做 ArrayList<Element>来使用 Element:元素对象
// 获取子元素对象
getElementById(String id)//:根据id属性值获取唯一的element对象
getElementsByTag(String tagName)//根据标签名称获取元素对象集合
getElementsByAttribute(String key)//根据属性名称获取元素对象集合
getElementsByAttributeValue(String key, String value)//根据对应的属性名和属性值获取元素对象集合
//获取属性值
String attr(String key)//根据属性名称获取属性值
//获取文本内容
String text()//获取文本内容
String html()//获取标签体的所有内容(包括字标签的字符串内容Node:节点对象 是Document和Element的父类
2.2 Demo4j
// 1、创建一个Dom4j的解析器对象,代表了整个dom4j框架
SAXReader saxReader = new SAXReader();
// 2、把XML文件加载到内存中成为一个Document文档对象
// Document document = saxReader.read(new File("xml-app\\src\\Contacts.xml")); // 需要通过模块名去定位
// Document document = saxReader.read(new FileInputStream("xml-app\\src\\Contacts.xml"));
// 注意: getResourceAsStream中的/是直接去src下寻找的文件
// 加载XML文件成为Document对象
InputStream is = Dom4JHelloWorldDemo1.class.getResourceAsStream("/Contacts.xml");
Document document = saxReader.read(is);
// 3、获取根元素对象
Element root = document.getRootElement();
System.out.println(root.getName());
// 4、拿根元素下的全部子元素对象(一级)
// List<Element> sonEles = root.elements();
List<Element> sonEles = root.elements("contact");
for (Element sonEle : sonEles) {
System.out.println(sonEle.getName());
}
// 拿某个子元素
Element userEle = root.element("user");
System.out.println(userEle.getName());
// 默认提取第一个子元素对象 (Java语言。)
Element contact = root.element("contact");
// 获取子元素文本
System.out.println(contact.elementText("name"));
// 去掉前后空格
System.out.println(contact.elementTextTrim("name"));
// 获取当前元素下的子元素对象
Element email = contact.element("email");
System.out.println(email.getText());
// 去掉前后空格
System.out.println(email.getTextTrim());
// 根据元素获取属性值
Attribute idAttr = contact.attribute("id");
System.out.println(idAttr.getName() + "-->" + idAttr.getValue());
// 直接提取属性值
System.out.println(contact.attributeValue("id"));
System.out.println(contact.attributeValue("vip"));2.3 四大检索方案
l绝对路径 采用绝对路径获取从根节点开始逐层的查找 /根元素/子元素/孙元素 从根元素开始,一级一级向下查找,不能跨级
相对路径 ./子元素/孙元素 从当前元素开始,一级一级向下查找,不能跨级
全文检索 直接全文搜索所有的name元素
| //contact | 找contact元素,无论元素在哪里 |
|---|---|
| //contact/name | 找contact,无论在哪一级,但name一定是contact的子节点 |
| //contact//name | contact无论在哪一种,name只要是contact的子孙元素都可以找到 |
- 属性查找 在全文中搜索属性,或者带属性的元素
| //@属性名 | 查找属性对象,无论是哪个元素,只要有这个属性即可。 |
|---|---|
| //元素[@属性名] | 查找元素对象,全文搜索指定元素名和属性名。 |
| //元素//[@属性名=‘值’] | 查找元素对象,全文搜索指定元素名和属性名,并且属性值相等。 |
代码实现,以Jsoup为例
//1.获取student.xml的path
String path = JsoupDemo6.class.getClassLoader().getResource("student.xml").getPath();
//2.获取Document对象
Document document = Jsoup.parse(new File(path), "utf-8");
//3.根据document对象,创建JXDocument对象
JXDocument jxDocument = new JXDocument(document);
//4.结合xpath语法查询
//4.1查询所有student标签
List<JXNode> jxNodes = jxDocument.selN("//student");
for (JXNode jxNode : jxNodes) {
System.out.println(jxNode);
}
System.out.println("--------------------");
//4.2查询所有student标签下的name标签
List<JXNode> jxNodes2 = jxDocument.selN("//student/name");
for (JXNode jxNode : jxNodes2) {
System.out.println(jxNode);
}
System.out.println("--------------------");
//4.3查询student标签下带有id属性的name标签
List<JXNode> jxNodes3 = jxDocument.selN("//student/name[@id]");
for (JXNode jxNode : jxNodes3) {
System.out.println(jxNode);
}
System.out.println("--------------------");
//4.4查询student标签下带有id属性的name标签 并且id属性值为itcast
List<JXNode> jxNodes4 = jxDocument.selN("//student/name[@id='itcast']");
for (JXNode jxNode : jxNodes4) {
System.out.println(jxNode);
}
SAXReader reader = new SAXReader();
Document document = reader.read(Demo.class.getResourceAsStream("/books.xml"));
//获取所有的book元素
List<Element> list = document.selectNodes("//book");
for (Element ele: list) {
System.out.println(ele.attributeValue("id"));
}
//获取id为0002的指定book元素的子标签name的内容
Element ele = (Element)document.selectSingleNode("//book[@id='0002']/name");
System.out.println(ele.getText());三、约束
3.1 DTD约束
DTD是文档类型定义(Document Type Definition)。DTD 可以定义在 XML 文档中出现的元素、这些元素出现的次序、它们如何相互嵌套以及XML文档结构的其它详细信息。
语法
文档声明
内部DTD,在XML文档内部嵌入DTD,只对当前XML有效。
xml<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE 根元素 [...//具体语法]><!--内部DTD--> <根元素> </根元素>外部DTD—本地DTD,DTD文档在本地系统上,企业内部自己项目使用。
xml<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE 根元素 SYSTEM "bookshelf.dtd"><!--外部本地DTD--> <根元素> </根元素>外部DTD—公共DTD,DTD文档在网络上,一般都有框架提供 , 也是我们使用最多的.
xml<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> </web-app>
元素声明
约束元素的嵌套层级 语法
<!ELEMENT 父标签 (子标签1,子标签2,…)>代码
dtd<!ELEMENT 书架 (书+)> <!--约束根元素是"书架","书架"子元素为"书",“+”为数量词,请看下面介绍--> <!ELEMENT 书 (书名,作者,售价)><!--约束"书"子元素依次为“书名”、“作者”、“售价”,“+”书元素至少1次-->约束元素体里面的数据 语法
<!ELEMENT 标签名字 标签类型>标签类型
| 标签类型 | 代码写法 | 说明 |
|---|---|---|
| PCDATA | (#PCDATA) | 被解释的字符串数据 |
| EMPTY | EMPTY | 即空元素,例如 |
| ANY | ANY | 即任意类型 |
代码
<!ELEMENT 书名 (#PCDATA)> <!--"书名"元素体为字符串数据-->
<!ELEMENT 作者 (#PCDATA)> <!--"作者"元素体为字符串数据-->
<!ELEMENT 售价 (#PCDATA)> <!--"售价"元素体为字符串数据-->
<!ELEMENT 出版日期 ANY> <!--"出版日期"元素体为任意类型-->
<!ELEMENT 版本号 EMPTY> <!--"版本号"元素体为空元素-->- 数量词
| 数量词符号 | 含义 |
|---|---|
| * | 表示元素可以出现0到多个 |
| + | 表示元素可以出现至少1个 |
| ? | 表示元素可以是0或1个 |
| , | 表示元素需要按照顺序显示 |
| | | 表示元素需要选择其中的某一个 |
属性声明
语法
<!ATTLIST 标签名称
属性名称1 属性类型1 属性说明1
属性名称2 属性类型2 属性说明2
…
>属性类型
| 属性类型 | 含义 |
|---|---|
| CDATA | 代表属性是文本字符串, eg:<!ATTLIST 属性名 CDATA 属性说明> |
| ID | 代码该属性值唯一,不能以数字开头, eg:<!ATTLIST 属性名 ID 属性说明> |
| ENUMERATED | 代表属性值在指定范围内进行枚举 Eg:<!ATTLIST属性名 (社科类|工程类|教育类) "社科类"> "社科类"是默认值,属性如果不设置默认值就是"社科类" |
属性说明
| 属性说明 | 含义 |
|---|---|
#REQUIRED | 代表属性是必须有的 |
#IMPLIED | 代表属性可有可无 |
#FIXED | 代表属性为固定值,实现方式:book_info CDATA #FIXED "固定值" |
代码
<!ATTLIST 书 <!--设置"书"元素的的属性列表-->
id ID #REQUIRED <!--"id"属性值为必须有-->
编号 CDATA #IMPLIED <!--"编号"属性可有可无-->
出版社 (清华|北大|传智播客) "传智播客" <!--"出版社"属性值是枚举值,默认为“传智播客”-->
type CDATA #FIXED "IT" <!--"type"属性为文本字符串并且固定值为"IT"-->
>3.2 schema约束
Schema 语言也可作为 XSD(XML Schema Definition)。 Schema 比DTD强大,是DTD代替者。 Schema 本身也是XML文档,单Schema文档扩展名为xsd,而不是xml。 Schema 功能更强大,数据类型约束更完善。
示例
<?xml version="1.0" encoding="UTF-8" ?>
<!--
传智播客DTD教学实例文档.将注释中的以下内容复制到要编写的xml的声明下面
复制内容如下:
<书架 xmlns="http://www.itcast.cn"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.itcast.cn bookshelf.xsd"
>
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.itcast.cn"
elementFormDefault="qualified">
<xs:element name='书架' >
<xs:complexType>
<xs:sequence maxOccurs='unbounded' >
<xs:element name='书' >
<xs:complexType>
<xs:sequence>
<xs:element name='书名' type='xs:string' />
<xs:element name='作者' type='xs:string' />
<xs:element name='售价' type='xs:double' />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>一个XML文档最多可以使用一个DTD文件,但一个XML文档中使用多个Schema文件,若这些Schema文件中定义了相同名称的元素时,使用的时候就会出现名字冲突。这就像一个Java文件中使用了import java.util.*和import java.sql.*时,在使用Date类时,那么就不明确Date是哪个包下的Date了。同理 , 在XML文档中就需要通过名称空间(namespace)来区分元素和属性是来源于哪个约束中的。名称空间就在在根元素后面的内容 , 使用xmlns到引入约束 。
当一个XML文档中需要使用多个Schema文件的时候 , 有且仅有一个使用缺省的 , 其他的名称空间都需要起别名 。参考资料中的 applicationContext.xml文件(spring框架的配置文件)