Skip to content
标签
XML
字数
3767 字
阅读时间
16 分钟

一、概述

概念:Extensible Markup Language 可扩展标记语言 * 可扩展:标签都是自定义的。<user> <student>功能: 存储数据、配置文件、在网络中传输

1.1 语法

  1. xml文档的后缀名 .xml
  2. xml第一行必须定义为文档声明
  3. xml文档中有且仅有一个根标签
  4. 属性值必须使用引号(单双都可)引起来
  5. 标签必须正确关闭
  6. xml标签名称区分大小写
  7. 默认使用UTF-8编码

1.2 结构概述

xml
<!– 注释内容 -->
<?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 属性列表 ?>

  1. 属性列表:
    • version:版本号,必须的属性
    • encoding:编码方式。告知解析引擎当前文档使用的字符集,默认值:ISO-8859-1
    • standalone:是否独立
      • 取值:
        • yes:不依赖其他文件
        • no:依赖其他文件

1.3 约束

规定xml文档的书写规则。 分类:

  1. DTD:一种简单的约束技术 引入dtd文档到xml文档中 可以约束XML文件的编写。不能约束具体的数据类型。 内部dtd:将约束规则定义在xml文档中 外部dtd:将约束的规则定义在外部的dtd文件中 本地: 网络:
  2. 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的方式:

  1. DOM:将标记语言文档一次性加载进内存,在内存中形成一颗dom树
    • 优点:操作方便,可以对文档进行CRUD的所有操作
    • 缺点:占内存
  2. SAX:逐行读取,基于事件驱动的。
    • 优点:不占内存。
    • 缺点:只能读取,不能增删改

二、解析器

常见的解析器:

  1. JAXP:sun公司提供的解析器,支持dom和sax两种思想
  2. DOM4J:一款非常优秀的解析器
  3. Jsoup:jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。
  4. PULL:Android操作系统内置的解析器,sax方式的。

2.1 jsoup

jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。

解析实现

java
//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

java
parse​(File in, String charsetName)//解析xml或html文件的。
parse​(String html)//解析xml或html字符串
parse​(URL url, int timeoutMillis)//通过网络路径获取指定的html或xml的文档对象

Document:文档对象。代表内存中的dom树

java
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 > * 查找某个父元素下所有直接子元素

java

// 组合选择器
// 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:元素对象

java
// 获取子元素对象
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

java
// 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//namecontact无论在哪一种,name只要是contact的子孙元素都可以找到
  • 属性查找 在全文中搜索属性,或者带属性的元素
//@属性名查找属性对象,无论是哪个元素,只要有这个属性即可。
//元素[@属性名]查找元素对象,全文搜索指定元素名和属性名。
//元素//[@属性名=‘值’]查找元素对象,全文搜索指定元素名和属性名,并且属性值相等。

代码实现,以Jsoup为例

java
//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文档结构的其它详细信息。

语法

文档声明
  1. 内部DTD,在XML文档内部嵌入DTD,只对当前XML有效。

    xml
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE 根元素 [...//具体语法]><!--内部DTD-->
    <根元素>
    </根元素>
  2. 外部DTD—本地DTD,DTD文档在本地系统上,企业内部自己项目使用。

    xml
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE 根元素 SYSTEM "bookshelf.dtd"><!--外部本地DTD-->
    <根元素>
    </根元素>
  3. 外部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>
元素声明
  1. 约束元素的嵌套层级 语法

    <!ELEMENT 父标签 (子标签1,子标签2,…)>

    代码

    dtd
    <!ELEMENT 书架 (书+)>  <!--约束根元素是"书架","书架"子元素为"书",“+”为数量词,请看下面介绍-->
    <!ELEMENT 书 (书名,作者,售价)><!--约束"书"子元素依次为“书名”、“作者”、“售价”,“+”书元素至少1次-->
  2. 约束元素体里面的数据 语法

    <!ELEMENT 标签名字 标签类型>

    标签类型

标签类型代码写法说明
PCDATA(#PCDATA)被解释的字符串数据
EMPTYEMPTY即空元素,例如
ANYANY即任意类型

代码

dtd
<!ELEMENT 书名 (#PCDATA)>  	<!--"书名"元素体为字符串数据-->
<!ELEMENT 作者 (#PCDATA)>		<!--"作者"元素体为字符串数据-->
<!ELEMENT 售价 (#PCDATA)>		<!--"售价"元素体为字符串数据-->
<!ELEMENT 出版日期 ANY>		   <!--"出版日期"元素体为任意类型-->
<!ELEMENT 版本号 EMPTY>		<!--"版本号"元素体为空元素-->
  1. 数量词
数量词符号含义
*表示元素可以出现0到多个
+表示元素可以出现至少1个
?表示元素可以是0或1个
,表示元素需要按照顺序显示
|表示元素需要选择其中的某一个
属性声明

语法

dtd
<!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 "固定值"

代码

dtd
<!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
<?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框架的配置文件)