xml概述
xml:x(extensible 可扩展) m(markup 标记) l(language 语言)
html 展示数据
xml 使用场景
xml特点
- 平台无关性,独立的语言
- 90%的语言都支持xml,不支持的10%语言发布的时候,xml还没出世
- xml具有自我描述性(内容自定义)
- html文件中,所有元素都是官方定义好的,我们直接引用
- xml文件中,所有元素自定义
xml语法规则
- xml文件必须有根元素
- xml元素(标签)有开必有合,成对出现
- xml元素大小写敏感
- xml元素必须正确的嵌套
- xml元素的属性必须加引号(单引双引都可以)
简单示例
定义一个xml文件,描述自己家庭中有大舅,二舅,老姑,每个人都有姓名,年龄和职业
1
2
3
4
5
6
7
8
9
10
11
12
|
<!--
定义一个xml文件,描述自己家庭中有大舅,二舅,老姑,每个人都有姓名,年龄和职业
-->
<family>
<member nickname="大舅">
<name>孙悟空</name>
<age>520</age>
<work>保镖</work>
</member>
</family>
|
CDATA区域使用
在xml中书写特殊符号时,需要使用CDATA 转义(同其他语言中的 \
), 语法 <![CDATA[xxxx]]>
例如对小于号进行转义
1
2
3
|
<xxx>
<![CDATA[5 < 10]]>
</xxx>
|
DTD文件应用
dtd:document type definition 文档类型定义,dtd的目的是帮助程序员编写合法的代码
dtd和xml的关系好比类(结构)与对象(具体数据),数据库中的表和行(一条记录)。
编写一个简单的dtd文件
1
2
3
4
5
6
7
8
|
<!--定义根元素students,且有多个子元素student-->
<!ELEMENT students (student*)>
<!--定义子元素student,且有name和age两个子元素-->
<!ELEMENT student (name, age)>
<!--定义子元素name, 类型是一个字符串-->
<!ELEMENT name (#PCDATA)>
<!--定义子元素age, 类型是一个字符串,dtd不支持整型等数据类型-->
<!ELEMENT age (#PCDATA)>
|
应用dtd文件
1
2
3
4
5
6
7
8
9
10
11
|
<?xml version="1.0" encoding="UTF-8" ?>
<!--根元素 文件地址-->
<!DOCTYPE students SYSTEM "test.dtd">
<students>
<student>
<name>张三</name>
<age>10</age>
</student>
</students>
|
引用dtd文件后,xml元素会有提示。
xsd文件应用
参考:xsd文件规则和语法
xsd:xml schemas definition xml结构定义
xsd是dtd的替代品,比dtd高端
xsd的代码基于xml,没有专门的语法,和xml一样的解析与处理。xsd支持一系列的数据类型。
编写一个简单的xsd文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns:tns="http://www.javacoder.cn/scores"
targetNamespace="http://www.javacoder.cn/scores"
xmlns="http://www.w3.org/2001/XMLSchema"
elementFormDefault="unqualified">
<simpleType name="sex">
<restriction base="string">
<enumeration value="MAN"/>
<enumeration value="WOMAN"/>
</restriction>
</simpleType>
<simpleType name="username">
<restriction base="string">
<pattern value="\w{2,6}"/>
</restriction>
</simpleType>
<simpleType name="score">
<restriction base="double">
<minInclusive value="0"/>
<maxInclusive value="100"/>
</restriction>
</simpleType>
<complexType name="classScore">
<sequence>
<element name="cid" type="string"/>
<element name="cname" type="string"/>
<element name="score" type="tns:score"/>
</sequence>
</complexType>
<complexType name="student">
<sequence>
<element name="username" type="tns:username"/>
<element name="sex" type="tns:sex"/>
<element name="averageScore" type="tns:score"/>
<element name="classScore" type="tns:classScore" minOccurs="1" maxOccurs="unbounded"/>
</sequence>
</complexType>
<element name="student" type="tns:student"/>
</schema>
|
说明
-
xmlns:tns 命名空间
命名空间是什么?
命名空间是唯一的URI
为什么存在xml命名空间?
- 消除两个刚好同名的元素的歧义
- 将共同idea的元素分组在一起
名称空间声明的一般形式为
-
targetNamespace 目标命名空间
表示本XSD中定义的element和type属于的namespace。防止命名冲突,和java的包的概念相同。
每一个Schema可以有且只有一个目标命名空间。在一个给定的Schema中,每一个名称都是属于一个特定的名字空间的, 同时可以由targetNamespace来命名。
xsd文件中定义了一个targetNameSpace后,其内部定义的元素,属性,类型等都属于该targetNameSpace
其自身或外部xsd文件使用这些元素,属性等都必须从定义的targetNameSpace中找
-
xmlns 源命名空间
在Schema中的定义和声明也可以引用其他的命名空间,我们可以把这种命名空间取名为源命名空间(source namespaces)。每一个Schema可以有多个源命名空间。
1
2
3
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
</xs:schema>
|
-
elementFormDefault: 本XSD的元素默认是否需要带前缀,“unqualified"表示不带,qualified表示带前缀
eg:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.w3school.com.cn"
xmlns="http://www.w3school.com.cn"
elementFormDefault="qualified">
<xs:element name="note">
<xs:complexType>
<xs:sequence>
<xs:element name="to" type="xs:string"/>
<xs:element name="from" type="xs:string"/>
<xs:element name="heading" type="xs:string"/>
<xs:element name="body" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
|
解析xml
概述
共有4种方式
- DOM解析
- 原理:解析xml的时候把文档中的所有元素按照其出现的层次关系,在内存中构造出树形结构
- 优点:可以遍历和修改节点的内容
- 缺点:内存压力较大,解析较慢
- SAX解析
- 优点:相对比dom,sax速度更快,更有效
- 缺点:不能修改节点内容
- JDOM解析
- DOM4J解析
- JDOM的一种智能的分支,合并了许多超出基本的xml文档的功能
- 著名的底层框架hibernate就是用dom4j来解析
dom4j性能最好,其次是sax,dom 和 jdom表现不好。
前两种属于基础方法,是官方提供的与平台无关的解析方式;
后两种属于扩展方法,它们是在基础的方法之上扩展出来的,只适用于java平台。
演示
引入依赖
1
2
3
4
5
6
|
<!-- https://mvnrepository.com/artifact/org.dom4j/dom4j -->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.0.0</version>
</dependency>
|
先写xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<?xml version="1.0" encoding="UTF-8" ?>
<!--根元素 文件地址-->
<!--<!DOCTYPE students SYSTEM "test.dtd">-->
<students>
<student type = "good">
<name>张三</name>
<age>10</age>
</student>
<student>
<name>李四</name>
<age>14</age>
</student>
</students>
|
编写代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
package com.eh.eden.xml;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.List;
public class XMLTest {
public static void main(String[] args) throws Exception {
// 1. 加载xml文件到jvm中,形成数据流
InputStream is = XMLTest.class.getClassLoader().getResourceAsStream("test.xml");
// 2. 创建解析对象
SAXReader sax = new SAXReader();
// 3. 获得文档对象(整个xml文件),将数据流转换成一个文档对象
Document doc = sax.read(is);
// 4. 获得根元素
Element root = doc.getRootElement();
// 5. 获得根元素下的所有子元素
List<Element> e1s = root.elements();
// 遍历元素
e1s.forEach(e1 -> {
// 标签属性
Attribute type = e1.attribute("type");
if (type != null) {
System.out.println(type.getData());
}
List<Element> e2s = e1.elements();
e2s.forEach(e2 -> {
// 标签名称和值
System.out.println(e2.getName() + "\t" + e2.getData());
});
});
}
}
// 输出
good
name 张三
age 10
name 李四
age 14
|
添加元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
package com.eh.eden.xml;
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;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
public class XMLTest {
public static void main(String[] args) throws Exception {
// 1. 加载xml文件到jvm中,形成数据流
InputStream is = XMLTest.class.getClassLoader().getResourceAsStream("test.xml");
// 2. 创建解析对象
SAXReader sax = new SAXReader();
// 3. 获得文档对象(整个xml文件),将数据流转换成一个文档对象
Document doc = sax.read(is);
// 4. 获得根元素
Element root = doc.getRootElement();
// 创建元素
Element student = root.addElement("student");
Element name = student.addElement("name");
name.setText("王五");
Element age = student.addElement("age");
age.setText("30");
// 写入到xml文件中
URL file = XMLTest.class.getClassLoader().getResource("");
System.out.println(file.getFile() + "test.xml");
OutputStream os = new FileOutputStream(new File(file.getFile() + "test.xml"));
OutputFormat format = new OutputFormat("\t", true, "UTF-8");
XMLWriter writer = new XMLWriter(os, format);
// 将整个文档对象写入到文件中
writer.write(doc);
writer.close();
}
}
|
xpath
xml文件 path路径
- xpath是一门在xml文档中快速查找信息的方式
- 单纯的使用dom4j访问节点时,需要一层一层的处理,如果有了xpath,访问层级的节点就简单了。
演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
package com.eh.eden.xml;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.List;
public class XMLTest {
public static void main(String[] args) throws Exception {
// 1. 加载xml文件到jvm中,形成数据流
InputStream is = XMLTest.class.getClassLoader().getResourceAsStream("test.xml");
// 2. 创建解析对象
SAXReader sax = new SAXReader();
// 3. 获得文档对象(整个xml文件),将数据流转换成一个文档对象
Document doc = sax.read(is);
// 4. 获得根元素
Element root = doc.getRootElement();
System.out.println("=======获取所有学生的名字========");
// 获取所有学生的名字
List<Node> names = root.selectNodes("student/name");
names.forEach(node -> System.out.println(node.getText()));
System.out.println("=======获取带有属性type的学生名字========");
// 获取带有属性type的学生名字
List<Node> namesWithType = root.selectNodes("student[@type]/name");
namesWithType.forEach(node -> System.out.println(node.getText()));
System.out.println("=======获取带有属性type并且属性值等于good的学生名字========");
// 获取带有属性type并且属性值等于good的学生名字
List<Node> namesWithTypeEqGood = root.selectNodes("student[@type='good']/name");
namesWithTypeEqGood.forEach(node -> System.out.println(node.getText()));
System.out.println("=======获取年龄超过10岁的学生名字========");
// 获取年龄超过10岁的学生名字
List<Node> namesWithAgeGT10 = root.selectNodes("student[age>10]/name");
namesWithAgeGT10.forEach(node -> System.out.println(node.getText()));
System.out.println("=======获取第一个学生的名字========");
// 获取第一个学生的名字
List<Node> firstStudentName = root.selectNodes("student[1]/name");
firstStudentName.forEach(node -> System.out.println(node.getText()));
System.out.println("=======获取最后一个学生的名字========");
// 获取最后一个学生的名字
List<Node> lastStudentName = root.selectNodes("student[last()]/name");
lastStudentName.forEach(node -> System.out.println(node.getText()));
System.out.println("=======获取倒数第二个学生的名字========");
// 获取倒数第二个学生的名字
List<Node> last2StudentName = root.selectNodes("student[last() - 1]/name");
last2StudentName.forEach(node -> System.out.println(node.getText()));
}
}
// 输出
=======获取所有学生的名字========
张三
李四
=======获取带有属性type的学生名字========
张三
=======获取带有属性type并且属性值等于good的学生名字========
张三
=======获取年龄超过10岁的学生名字========
李四
=======获取第一个学生的名字========
张三
=======获取最后一个学生的名字========
李四
=======获取倒数第二个学生的名字========
张三
|