目录

xml在java开发中的应用

xml概述

xml:x(extensible 可扩展) m(markup 标记) l(language 语言)

html 展示数据

xml 使用场景

  • 保存数据

    手机中的单机游戏,等级,装备等数据记录在哪?

    数据库太大,普通文件太慢,所以采用xml文件保存软件中的数据

  • 网络上传递数据

  • 配置文件 *.properties *.xml 一个properties 九个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命名空间?

    1. 消除两个刚好同名的元素的歧义
    2. 将共同idea的元素分组在一起

    名称空间声明的一般形式为

    • 第一部分是一个关键字xmlns:

    • 第二部分是名称空间的前缀(标识符)

    • 第三部分是一个等号

    • 第四部分是双引号

    • 第五部分是名称空间标识URI

      需要注意的是,名称空间的前缀不能为xml,因为在XML中这个字符串是保留作特殊用途的。

  • 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岁的学生名字========
李四
=======获取第一个学生的名字========
张三
=======获取最后一个学生的名字========
李四
=======获取倒数第二个学生的名字========
张三