快速上手系列:XML
extensible markup language :可扩展标记型语言
(1)标记型语言:html 也是标记型语言,即使用标签来操作
(2)可扩展:
1)html 里面的标签是固定的,每个标签都有特定的含义 <h1> <br/>
2)标签可以自己定义,可以写中文的标签 <dog></dog>、<猫></猫>
(3) xml 用途
1)html 是用于显示数据,xml 也可以显示数据(但不是主要功能)
2)xml 主要功能,为了存储数据
(4)xml 是 w3c 组织发布的技术,xml 有两个版本 1.0 1.1,一般我们使用都是 1.0 版本,1.1 版本不能向下兼容。
1、不同的系统之间传输数据。比如 qq 之间数据的传输;用来表示生活中有关系的值,比如:
<中国> <河南> <焦作></焦作> <洛阳></洛阳> </河南> </中国>
三 xml 语法
(1)xml 的文档声明
a) 创建一个文件,后缀名是.xml
b) 如果写 xml,第一步必须要有一个文档声明
c) <?xml version=”1.0” encoding=”gbk”?> (格式严格按照这个来)(注意:乱码的解决:设置保存时候的编码和打开时候的编码一致)
d) 文档声明必须写在第一行第一列!
e) 属性:
- version:xml 的使用版本
- encoding:xml 编码
- standalone:是否需要依赖其他文件 yes/no (很少用)
(2)定义元素(标签定义)
a) 标签定义有开始必须要有结束:<person></person>
b) 标签没有内容,可以在标签内结束:<person/>
c) 标签可以嵌套,必须要合理嵌套
i. 合理嵌套如<aa><bb></bb></aa>
ii. 不合理嵌套如<aa><bb></aa></bb>这种方式是不对的
d) 一个 xml 中,只能有一个根标签,其他标签都是这个标签下面的标签
e) 在 xml 中把空格和换行都当成内容来解析
比如下面这两段代码含义是不一样的:
<aa>111</aa> <aa> 111 </aa>
f) xml 标签可以是中文
g) xml 中标签的名称规则
i. xml 代码区分大小写,如<p> <P>不一样
ii. xml 的标签不能以数字和下划线(_)开头,如<2a> <_a>不正确
iii. xml 的标签不能以 xml、Xml、XML 等开头
iv. xml 的标签不能包含空格和冒号,如<a b> <b:c>不正确
(3)定义属性
a) xml 作为标记型文档,可以有属性
b) 属性定义的要求
i. 一个标签上可以有多个属性
<person id1=”aa” id2=”bb”></person>
ii. 属性名称不能相同
iii. 属性名称和属性值之间使用=,属性值使用引号包起来
iv. 属性名称规范和元素名称规范一致
(4)注释
写法<!-- 注释内容 -->(my eclipse 里快捷键为 ctrl +shift+/)
(5)特殊字符
转义字符(和 html 一样的):如&——& <——< >——>
(6)CDATA 区
a) 可以解决多个字符都需要转义的操作
b) 把这些内容放到 CDATA 区里面,就不需要转义,可以把特殊字符当作文本内容直接输出了
c) 写法 <![CDATA[ 内容] ]>
(7)PI 指令(处理指令)
A) 可以在 xml 中设置样式(就是设置了个 css 外部样式表,只是导入的写法不同罢了)
B) 写法: <?xml-stylesheet type=”text/css” href=”css 的路径”?>
C) 注意:设置样式只能对英文标签名有用,对中文的标签名没用。
(8)xml 的约束
A) 为什么需要约束
B) xml 标签是自定义的,需要技术来规定 xml 中能出现的元素,这个时候需要约束。
C) xml 的约束技术:dtd 约束和 schema 约束
四 约束技术快速入门(dtd)
1 dtd 入门
(1)创建一个文件,后缀名为.dtd
(2)步骤:
a) 看 xml 中有多少个元素,有几个元素,在 dtd 文件中写几个<!ELEMENT>
b) 判断元素是简单元素还是复杂元素
i. 复杂元素:有子元素的元素
<!ELEMENT 元素名称 (子元素)>
如:<!ELEMENT person (name,age)>
Ii. 简单元素:没有子元素
<!ELEMENT 元素名称 (#PCDATA)>
如:<!ELEMENT name (#PCDATA)>
c) 需要在 xml 文件中引入 dtd 文件
<!DOCTYPE 根元素名称 SYSTEM “dtd 文件的路径”>
如:<!DOCTYPE person SYSTEM “1.dtd”>
(3)打开 xml 文件使用浏览器打开,浏览器只负责校验 xml 的语法,不负责校验约束
(4)如果想要校验 xml 的约束,需要使用工具,如 myEclipse(编译时会报错)
2 dtd 的三种引入方式
(1)引入外部的 dtd 文件
<!DOCTYPE 根元素名称 SYSTEM “dtd 路径”>
(2)使用内部的 dtd 文件
<!DOCTYPE 根元素名称 [
<!ELEMENT person (name,age)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
]>
(3)使用外部的 dtd 文件(网络上的 dtd 文件)
<!DOCTYPE 根元素名称 PUBLIC “dtd 名称” “dtd 文档的 url”>
- 后面学到框架 struts2,使用配置文件,就是用的外部的 dtd 文件
-如 :<!DOCTYPE struts PUBLIC “-//Apache Software Foundation//DTD Struts Configuration 2.0//EN” “http://struts.apache.org/dtds/struts-2.0.dtd”>
3 使用 dtd 定义元素
(1) 语法:<!ELEMENT 元素名 约束>
(2) 简单元素:没有子元素的元素
a) (#PCDATA) :约束 name 是字符串类型
b) EMPTY :元素为空(没有内容)
- 如:<!ELEMENT sex EMPTY>
<sex></sex>
c) ANY : 任意类型元素
注意:除了 PCDATA,另外俩不带括号和#号
(3) 复杂元素
a) <!ELEMENT person (name,age,sex,school)>
- 子元素只能出现一次
b) <!ELEMENT 元素名称 (子元素)>
c) 表示子元素出现的次数
i. + :表示一次或者多次
ii. ? :表示零次或者一次
iii. * :表示零次或多次
如:<!ELEMENT person (name+,age?,sex*,school)>
d) 子元素直接使用逗号进行隔开
** 表示元素出现的顺序
e) 子元素直接使用 | 隔开
** 表示元素只能出现其中的任意一个
如:<!ELEMENT person (name|age|sex|school)>
4 使用 dtd 定义属性
(1)语法:<!ATTLIST 元素名称 属性名称 属性类型 属性的约束>
(2)属性类型
a) CDATA : 字符串
- <!ATTLIST birthday ID1 CDATA #REQUIRED>
b) 枚举:表示只能在一定的范围内出现值,但是只能每次出现其中一个
i. 例如红绿灯效果
ii. (aa|bb|cc)
- <!ATTLIST age ID2 (AA|BB|CC) #REQUIRED>
c) ID: 值只能是字母或者下划线开头
- <!ATTLIST name ID3 ID #REQUIRED>
(3)属性的约束
a) #REQUIRED: 属性必须存在
b) #IMPLIED: 属性可有可无
c) #FIXED: 表示一个固定值
i. 属性的值必须是设置的这个固定值
ii. <!ATTLIST sex ID4 CDATA #FIXED “ABC”>
d) 直接值
i. 不写属性,使用直接值
ii. 写了属性,使用设置那个值
iii. <!ATTLIST school ID5 CDATA “WWW” >
e) 定义属性示例
<!ATTLIST 页面作者 // 元素名称是页面作者,下面有多个属性
姓名 CDATA #IMPLIED // 姓名是字符串类型,可有可无
年龄 CDATA #IMPLIED
联系信息 CDATA #REQUIRED // 这个属性值必须有
网站职务 CDATA #FIXED “页面作者” // 网站职务的值必须是“页面作者”
个人爱好 CDATA “上网” //如果有这个属性,就以你设置的为主,没有就用默认这个>
5 dtd 实体的定义
(1)语法:<!ENTITY 实体名称 “实体的值”>
如 <!ENTITY TEST “haha”>
(2)使用实体:
直接在标签名里写:&实体名称;
如 <name>&TEST;</name>
(3)注意:
定义实体需要写在内部 dtd 里面,如果写在外部的 dtd 里面,有些浏览器会得不到内容。
五 约束技术快速入门(schema)
1 schema 约束
* dtd 语法:<!ELEMENT 元素名称 约束>
* schema 符合 xml 的语法,xml 语句
* 一个 xml 中可以有多个 schema,多个 schema 使用名称空间区分(类似于Java 包名)
* dtd 里面有 PCDATA 类型,但是在 schema 里面可以支持更多的数据类型。比如,年龄只能是整数,在 schema 可以直接定义一个整数类型
* schema 语法更加复杂,schema 目前不能替代 dtd
* 一个 xml 文件只能有一个 dtd,多个 schema
2 schema 的快速入门
(1) 创建一个 schema 文件 后缀名是 .xsd
(2) 在 schema 文件里面
* 属性:xmlns=”http://www.w3.org/2001/XMLSchema” - 表示当前 xml 文件是一个约束文件
* targetNamespace=”http://www.itcast.cn/20151111” - 使用 schema 约束文件,直接通过这个地址引入约束文件
* elementFormDefault=”qualified”
(3)步骤:
1) 看 xml 中有多少个元素,有几个就写几个<element>
2) 看简单元素和复杂元素,复杂元素写法如下:
<complexType> <sequence> 子元素 </sequence> </complexType>
3) 简单元素,写在复杂元素<sequence>里
<element name=”person”> <complexType> <sequence> <element name=”name” type=”sting”></element> <element name=”age” type=”int”></element> </sequence> </complexType> </element>
4) 在被约束文件里引入约束文件
<person xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns=”http://www.itcast.cn/20151111” xsi:schemaLocation=”http://www.itcast.cn/20151111 1.xsd”> * xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” - 表示 xml 是一个被约束的文件(为了区别下面的 xmlns,给它随便起个名字,比如 xsi) * xmlns=”http://www.itcast.cn/20151111” - 是约束文档里面 targetNamespace * xsi:schemaLocation=”http://www.itcast.cn/20151111 1.xsd” - targetNamespace 空格 约束文档的地址路径
(4)<sequence> : 表示元素出现的顺序
<all> : 每个元素只能出现一次
<choice> : 元素只能出现其中的一个
maxOccurs=”unbounded”: 表示元素的出现次数(unbounded 为无限)
<any></any> : 表示任意元素
(5)可以约束属性
* 写在复杂元素里面
* 写在</complexType>之前, 如:<attribute name=”id1” type=”int” use=”required”></attribute>
- name: 属性名称
- type: 属性类型 int ,string
- use: 属性是否出现 required
(6)复杂的 schema 约束
<company xmlns=”http://www.example.org/company” xmlns:dept=”http://www.example.org/department” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xsi:schemaLocation=”http://www.example.org/company company.xsd http://www.example.org/department department.xsd”> <employee age=”30”> //想要引入部门的约束文件里面的 name,使用部门的别名 detp:元素名称 <dept:name>100</dept:name> <name>张三</name> </employee>
</company>
六 xml 的解析(写进 java 代码)
1 xml 是标记型文档
2 js 使用 dom 解析标记型文档:
- 根据 html 的层级结构,在内存中分配一个树形结构,把 html 的标签、属性和文本都封装成对象
- document 对象、element 对象、属性对象、文本对象、Node 节点对象
3 xml 的解析方式(技术):dom 和 sax
** dom 方式解析:
- 根据 xml 的层级结构在内存中分配一个树形结构,把 xml 标签、属性和文本都封装成对象
- 缺点:如果文件过大,造成内存溢出
- 优点:很方便实现增删改操作
** sax 方式解析:
- 采用事件驱动,边读边解析,从上到下,一行一行的解析,解析到某个对象,返回对象名称
- 缺点:不能实现增删改操作
- 优点:如果文件过大,不会造成内存溢出,方便实现查询操作
** 想要解析 xml, 首先需要解析器
(1)不同的公司和组织提供了 针对 dom 和 sax 方式的解析器,通过 api 提供
(2)Sun 公司提供了针对 dom 和 sax 解析器:jaxp (其次)
(3)Dom4j 组织,针对 dom 和 sax 解析器:dom4j(常用)
(4)Jdom 组织,针对 dom 和 sax 解析器:jdom(最次)
4 jaxp 的 api 查看
(1)jaxp 是 javase 的一部分
(2)Jaxp 解析器在 jdk 的 javax.xml.parsers 包里面
a) 四个类:分别是针对 dom 和 sax 解析使用的类
b) dom:
i. DocumentBuilder:解析器类
1. 这个类是一个抽象类,不能 new,此类的实例可以从DocumentBuilderFactory.newDocumentBuilder()方法获取
2. 一个方法,可以解析 xml parse(“xml 路径”) 返回的是 Document 整个文档
3. 返回的 document 是一个接口,父节点是 Node,如果在 document 里面找不到想要的方法,到 Node 里面去找
4. 在 document 里面的方法(跟 js 的方法类似,但这在 java 里)
a) GetElementsByTagName(String tagname)
i. 这个方法可以得到标签
ii. 返回集合 NodeList
b) createElement(String tagName)
i. 创建标签
c) createTextNode(String data)
i. 创建文本
d) appendChild(Node newChild)
i. 把文本添加到标签下面
e) removeChild(Node oldChild)
i. 删除节点
f) getParentNode()
i. 获取父节点
接口:NodeList
- getLength() 得到集合的长度
- item(int index) 下标取到具体的值(这个要遍历)
for(int i=0;i<list.getLength();i++){ list.item(i); }
ii. DocumentBuilderFactory:解析器工厂
这个类也是一个抽象类,不能 new,newInstance()获取 DocumentBuilderFactory 的实例
c) sax:
i. SAXParser : 解析器类
此类的实例可以从 SAXParserFactory.newSAXParser()方法获取
- parse(File f, DefaultHandler dh) :两个参数, 第一个参数:xml 的路径,第二个参数:事件处理器
ii. SAXParserFactory : 解析器工厂
实例通过 newInstance() 方法得到
5 使用 jaxp 实现查询操作
* 查询 xml 中所有的 name 元素的值(实例如下)
* 步骤:(下面都写在 java 的方法里面;全导 w3c 的包)
//查询所有 name 元素的值 // 1、创建解析器工厂 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); // 2、根据解析器工厂创建解析器 DocumentBuilder builder = builderFactory.newDocumentBuilder(); // 3、解析 xml 返回 document Document document = builder.parse(“src/person.xml”); // 4、得到所有的 name 元素 NodeList list = document.getElementsByTagName(“name”); // 5、返回集合,遍历集合,得到每一个 name 元素 for(int i=0;i<list.getLength();i++){ Node name1=list.item(i); //得到每一个 name 元素 //得到 name 元素里面的值 String s = name1.getTextContent(); System.out.println(s); }
6 使用 jaxp 添加节点
* 在第一个<p1>下面(末尾位置)添加<sex>nv</sex>
* 步骤:(前三步还那样,老套路)
//1、创建解析器工厂 //2、根据解析器工厂创建解析器 //3、解析 xml,返回 document //4、得到第一个 p1 - 得到所有 p1,然后使用 item 方法下标得到第一个 NodeList list = document.getElementsByTagName(“p1”); Node p1 = list.item(0); //5、创建 sex 标签 createElement Element sex1 = document.createElement(“sex”); //6、创建文本 createTextNode Text text1 = document.createTextNode(“nv”); //7、把文本添加到 sex 下面 appendChild sex1.appendChild(text1); //8、把 sex 添加到第一个 p1 下面 p1.appendChild(sex1); (到此为止,只是在树形结构内存里添加,还需要写到文档里) //9、回写 xml(这也是个三步老套路) TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); transformer.transform(new DOMSource(document), new StreamResult(“src/person.xml”));
7 使用 jaxp 修改节点
* 修改第一个 p1 下面的 sex 内容是 nan
* 步骤:(前 3 步,第 6 步老样子)
1、创建解析器工厂 2、根据解析器工厂创建解析器 3、解析 xml,返回 document 4、得到 sex item 方法 Node sex1 = document.getElementByTagName(“sex”).item(0); 5、修改 sex 里面的值 setTextContent 方法 sex1.setTextContent(“nan”); 6、回写 xml
8 使用 jaxp 删除节点
* 删除<sex>nan</sex>节点
* 步骤:(老套路就不写了)
1、创建解析器工厂 2、根据解析器工厂创建解析器 3、解析 xml,返回 document 4、获取 sex 元素 Node sex1 = document.getElementsByTagName(“sex”).item(0); 5、获取 sex 的父节点 使用 getParentNode 方法 Node p1 = sex1.getParentNode(); 6、删除使用父节点删除 removeChild 方法 p1.removeChild(sex1); 7、回写 xml
9 使用 jaxp 遍历节点
* 把 xml 中的所有元素名称打印出来
* 步骤:
1、创建解析器工厂 2、根据解析器工厂创建解析器 3、解析 xml,返回 document 4、编写一个方法实现遍历(递归实现) list1(document);(方法如下) private static void list1 (Node node){ //判断是元素类型才能打印出(避免打出换行和空格) if(node.getNodeType() == Node.ELEMENT_NODE){ System.out.println(node.getNodeName()); } //得到一层子节点 NodeList list = node.getChildNodes(); for(int i=0;i<list.getLength();i++){ //得到每一个节点 Node node1 = list.item(i); //为了继续得到 node1 的子节点,递归 list1(node1); } }
七 使用 dom4j 解析 xml (本文重点)
1 简介
* dom4j 是一个组织,针对 xml 解析,提供解析器 dom4j
* dom4j 不是 javase 的一部分,想要使用第一步需要怎么做
- 导入 dom4j 提供 jar 包
- 创建一个文件夹 lib
- 复制 jar 包到 lib 下面
- 右键点击 jar 包,build path - - add to buildpath
- 看到 jar 包,变成奶瓶样子,表示导入成功
* 得到 document
SAXReader reader = SAXReader();
Document document = reader.read( url );
* document 的父接口是 Node
- 如果在 document 里面找不到想要的方法,到 Node 里面去找
* document 里面的方法 getRootElement() : 获取根节点,返回 Element
* Element 也是一个接口,父接口是 Node
- Element 和 Node 里面方法:
** getParent() : 获取父节点
** addElement (): 添加标签
- element 里面的方法:
* element(qname) : qname 是标签名称
- 表示获取标签下面的第一个子标签
* elements(qname) :
- 获取标签下面是这个名称的所有子标签(一层)
* elements() :
- 获取标签下面的所有一层子标签
2 使用 dom4j 查询 xml(导 dom4j 的包)
* 查询 xml 中所有 name 元素的值
//1、创建解析器 SAXReader saxReader = new SAXReader(); //2、得到 document Document document = saxReader.read(“src/p1.xml”); //3、得到根节点 Element root = document.getRootElement(); //4、得到 p1 List<Element> List = root.elements(“p1”); //5、遍历 list for(Element element : list){ //element 是每一个 p1 元素 //得到 p1 下面的 name 元素 Element name1 = element.element(“name”); //得到 name 里面的值 String s = name1.getText(); System.out.println(s); }
* 查询第一个 name 元素的值(前三步同上)
1、创建解析器 2、得到 document 3、得到根节点 //4、得到第一个 p1 Element p1 = root.element(“p1”); //5、得到 p1 下面的 name 元素 Element name1 = p1.element(“name”); //6、得到 name 的值 String s1 = name1.getText(); System.out.println(s1);
* 查询第二个 name 元素的值(前三步同上)
1、创建解析器 2、得到 document 3、得到根节点 //4、得到所有的 p1 List<Element> list = root.elements(“p1”); //5、得到第二个 p1 list 集合下标从 0 开始 Element p2 = list.get(1); //6、得到 p1 下面的 name Element name2 = p2.element(“name”); //7、得到 name 里面的值 String s2 = name2.getText(); System.out.println(s2);
3 使用 dom4j 实现添加操作
* 在第一个 p1 标签末尾添加一个元素<sex>nv</sex>
1、创建解析器 2、得到 document 3、得到根节点 //4、得到第一个 p1 元素 Element p1 = root.element(“p1”); //5、在 p1 下面直接添加元素 Element sex1 = p1.addElement(“sex”); //6、在 sex 下面添加文本 sex1.sexText(“nv”); //回写 xml OutputFormat format = OutputFormat.createPrettyPrint();//可以有缩进的 效果 XMLWriter xmlWriter =new XMLWriter(new FileOutputStream(“src/p1.xml”),format ) ; xmlWriter.write(document); xmlWriter.close();
4 使用 dom4j 在特定位置添加节点
* 在第一个 p1 下面的 age 标签之前添加<school>ecit.edu.cn</school>
1、创建解析器 2、得到 document 3、得到根节点 //4、得到第一个 p1 元素 Element p1 = root.element(“p1”); //5、得到 p1 下面的所有元素 List<Element> list = p1.elements(); //6、创建元素 使用 Element school = DocumentHelper.createElement(“school”); //7、在 school 下面创建文本 school.setText(“ecit”); //在特定位置添加 list.add(1,school); 8、回写
5 使用 dom4j 修改节点
* 修改第一个 p1 下面的 age 元素的值<age>20</age>
1、创建解析器 2、得到 document 3、得到根节点 4、得到第一个 p1 //5、得到 p1 下面的 age Element age = p1.element(“age”); //6、修改 age 的值 age.setText(“300”); 7、回写 xml
6 使用 dom4j 删除节点
* 删除第一个 p1 下面的<school>ecit</school>元素
1、创建解析器 2、得到 document 3、得到根节点 4、得到第一个 p1 //5、得到 p1 下面的 school 标签 Element sch = p1.element(“school”); //6、删除 school 元素 //通过父节点删除,而获取父节点的方法是 sch.getParent();且得出 school 的父节点 p1 p1.remove(sch); 7、回写 xml
7 使用 dom4j 获取属性值
* 获取第一个 p1 里面的属性 id1 的值
1、创建解析器 2、得到 document 3、得到根节点 4、得到第一个 p1 //5、得到 p1 里面的属性值 String value = p1.attributeValue(“id1”); System.out.println(value);
8 使用 dom4j 支持 xpath
* 可以直接获取到某个元素(xpath 算是个公式吧)
(1)第一种形式
a) /AAA/DDD/BBB : 表示一层一层的,AAA 下面 DDD 下面的 BBB
(2)第二种形式
a) //BBB: 表示和这个名称相同,只要名称是 BBB,都得到
(3)第三种
a) /* :所有元素
(4)第四种
a) BBB[1] :表示第一个 BBB 元素
b) BBB[last()] : 表示最后一个 BBB 元素
(5)第五种
a) //BBB[@id] : 表示只要 BBB 元素里有 id 属性,都得到
(6)第六种
a) //BBB[@id=’b1’] : 表示元素名称是 BBB,在 BBB 上面有 id 属性,并且 id 的属性值是 b1
9 使用 dom4j 支持 xpath 具体操作
** 默认情况下,dom4j 不支持 xpath
** 要想在 dom4j 里面使用 xpath
* 首先引入支持 xpath 的 jar 包,使用 jaxen-1.1-beta-6.jar
* 再把 jar 包导入到项目中
** 在 dom4j 里面提供了两个方法,用来支持 xpath
* selectNodes(“xpath 表达式”) : 获取多个节点
* selectSingleNode(“xpath 表达式”) : 获取一个节点
** 使用 xpath 实现:查询 xml 中所有 name 元素的值
** 所有 name 元素的 xpath 表示: //name
** 使用 selectNodes(“//name”);
** 代码和步骤:
1、得到 document //2、直接使用 selectNodes(“//name”)方法得到所有的 name 元素 List<Node> list = document.selectNodes(“//name”); //遍历 list 集合 for(Node node:list){ //node 是每一个 name 元素 //得到 name 元素里面的值 Stirng s = node.getText(); System.out.println(s); }
** 使用 xpath 实现:获取第一个 p1 下面的 name 的值
* //p1[@id1=’aaaa’]/name
* 使用到 selectSingleNode(“//p1[@id1=’aaaa’]/name”)
* 步骤和代码:
1、得到 document //2、直接使用 selectSingleNode 方法实现 Node name1 = document.selectSingleNode(“//p1[@id1=’aaaa’]/name”);//name的元素 //3、得到 name 里面的值 String s1 = name1.getText(); System.out.println(s1);