XML

XML入门

一、xml的简介

1、extensible Markup Language:可扩展标记型语言

  • 标记型语言: html是标记型语言

    • 也是使用标签来操作
  • 可扩展:

    • html里面的标签是固定,每个标签都有特定的含义



    • 标签可以自己定义,可以写中文的标签、<猫>/<猫>

2、xml用途

  • html是用于显示数据,xml也可以显示数据(不是主要功能)

  • xml主要功能,为了存储数据

3、xml是w3c组织发布的技术

4、xml有两个版本1.0和1.1

  • 使用都是1.0版本:(1.1版本不能向下兼容)

二、xml的应用

1、经常用在文件配置

  • 比如现在连接数据库肯定知道数据库的用户名和密码,数据名称
  • 如果修改数据库的信息,不需要修改源代码,只要修改配置文件就可以了

2、不同的系统之间传输数据

  • qq之间数据的传输

  • image-20240331152544908

  • image-20240331152612319

3、用来表示生活中有关系的数据

image-20240331152655519

image-20240331152715631

三、xml的语法

(1) xml的文档声明(***)

  • 创建一个文件后缀名是.xml

  • 如果写xml,第一步必须要有一个文档声明(写了文档声明之后,表示写xml文件的内容)

    • <?xml version="1.0" encoding="gbk"?>
      
    • 文档声明必须写在第一行第一列

  • 属性

    • version: xml的版本1.0(使用)1.1

    • encoding: xml编码有 gbk utf-8 iso8859-1(不包含中文)

    • standalone:是否需要依赖其他文件 yes/no

  • 中文乱码问题解决

    • 原因:xml文件保存时不是以utf-8的编码保存,而是以gbk编码格式保存到本地硬盘。
    • 解决方法:保存文件以utf-8格式保存即可。
    • image-20240331155230081
  • xml文件示例

    <?xml version="1.0" encoding="utf-8" ?>
    <person>
        <name>张三</name>
        <age>20</age>
    </person>
    

(2)定义元素(标签)(***)

  • 标签定义

    • 标签定义有开始必须要有结束:

      <person></person>
      
    • 标签没有内容,可以在标签内结束:

      <aa/>
      
  • 标签可以嵌套,必须要合理嵌套

    • 合理嵌套

      <aa><bb></bb></aa>
      
    • 不合理嵌套:这种方式是不正确的

  • 一个xml中,只能有一个根标签,其他标签都是这个标签下面的标签在xml中把空格和换行都当成内容来解析

    • 下间这两段代码含义是不一样的

      <aa>1111111</aa>
      
    • <aa>
      111111111
      </aa>
      
  • xml标签可以是中文

  • xml中标签的名称规则

    • (1)xml代码区分大小马

      <p>和<P>:这两个标签是不一样
      
    • (2) xml的标签不能以数字和下划线()开头

      <2a>和<aa>:这样是不正确的
      
    • (3)xml的标签不能以xml、XML、Xml等开头

      <xmla> <XmlB> <XMLC>:这些都是不正确的
      
    • (4) xml的标签不能包含空格和冒号

      <a b> <b:c> :这些是不正确的
      

(3)定义属性(***)

  • html是标记型文档,可以有属性

  • xml也是标记型文档,可以有属性

    • <person id1="aaa" id2="bbb"></person>
      
  • 属性定义的要求

    • (1)一个标签上可以有多个属性

      <person id1="aaa" id2="bbb"></person>
      
    • (2)属性名称不能相同

      <person id1="aaa" id1="bbb"></person>:这个是不正确,不能有两个id1
      
    • (3)属性名称和属性值之间使用= ,属性值使用引号包起来(可以是单引号,也可以是双引号)

    • (4) xml属性的名称规范和元素(标签)的名称规范一致

(4)注释(***)

  • 写法

  • 注意的地方

    • 注释不能嵌套

      <!--<!-- -->-->:这是不正确的
      
  • 注释也不能放到第一行,第一行第一列必须放文档声明

(5)特殊字符(***)

  • 如果想要在xml中现在a<b ,不能正常显示,因为把<当做标签如果就想要显示,需要对特殊字符<进行转义

    image-20240331162225010

    例如:

    <a>a&lt;b</a>  代表了a<b
    

(6)xml的CDATA区(了解)

  • 可以解决多个字符都需要转义的操作如 if(a<b && b<c && d>f) {}把这些内容放到CDATA区里面,不需要转义了

  • 把特殊字符,当做文本内容,而不是标签

  • 写法

    <![CDATA[内容]]>-代码
    <![CDATA[if (a<b & & b<c && d>f){}]]>
    

(7)xml的PI指令(处理指令)

  • 作用:用来指挥软件如何解析XML文档。
  • 语法:必须以“”作为结尾。
  • 常用处理指令:
    • xml声明:
    • xml-stylesheet指令:
      • 作用:指示XML文档所使用的CSS择式xSL。
      • 注:对中文命名的标签元素不起作用

总结

  • 所有XML元素都须有关闭标签
  • XML标签对大小写敏感
  • XML必须正确地嵌套顺序
  • XML文档必须有根元素(只有一个)
  • XML的属性值须加引号
  • 特殊字符必须转义---CDATA
  • XML中的空格、回车换行会解析时被保留

四、xml的约束

为什么需要约束?
比如现在定义一个person的xml文件,只想要这个文件里面保存人的信息,比如name age等,但是如果在xml文件中写了一个标签<猫>,发现可以正常显示,因为符合语法规范。但是猫肯定不是人的信息,xml的标签是自定义的,需要技术来规定xml中只能出现的元素,这个时候需要约束。

xml的约束的技术:DTD约束和Schema约束

五、DTD

1、DTD的快速入门

  • 步骤:

  • 创建一个文件后缀名.dtd

  • (1)看xml中有多少个元素,有几个元素,在dtd文件中写几个<!ELEMEN

  • (2)判断元素是简单元素还是复杂元素

    • 复杂元素:有子元素的元素

      • <!ELEMENT 元素名称 (子元素)>
        <!ELEMENT person (name,age)>
        
    • 简单元素:没有子元素

      • <!ELEMENT 元素名称 (#PCDATA)>
        <!ELEMENT name (#PCDATA)>
        
  • (3)需要在xml文件中引入dtd文件

    <!DOCTYPE 根元素名称 SYSTEM "dtd文件的路径">
    放在XML文件的第二行最好
    
  • 打开xml文件使用浏览器打开的,浏览器只负责校验xml的语法,不负责校验约束

  • 如果想要校验xml的约束,需要使用工具(myeclipse工具)

    • 打开myeclipse开发工具
    • 仓建一个java project项目day05
    • 在day05的src目录下面创建一个xml文件和一个dtd文件
    • 当xml中引入dtd文件之后,比如只能出现name,age,多写了一个a,会提示出错

2、DTD的三种引入方式

(1)引入外部的dtd文件

<!DOCTYPE 根元素名称 SYSTEM "dtd路径">

(2)使用内部的dtd文件(即直接写在xml文件内)

<!DOCTYPE根元素名称[
    <!ELEMENT person (name,age)>
    <!ELEMENT name (#PCDATA)>
    <!ELEMENTage (#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定义元素

  • 语法:

    <!ELEMENT 元素名 约束>
    
  • 简单元素:没有子元素的元素

    <!ELEMENT name (#PCDATA)>
    
    • (#PCDATA):约束name是字符串类型

    • EMPTY :元素为空(没有内容)

      <sex></sex>
      <!ELEMENT sex (#EMPTY)>
      
    • ANY:元素可以是任意内容

  • 复杂元素:

    <!ELEMENT person (name,age,sex,school)>
    
    • 子元素只能出现一个
    <!ELEMENT 元素名称 (子元素)>
    
    • 表示子元素出现的次数

      +:表示一次或者多次

      ?:表示零次或者一次

      *:表示零次或者多次

      使用方法:

      <!ELEMENT person (name+,age?,sex*,school)>
      
    • 子元素直接使用逗号进行隔开,

      • 表示子元素出现的项序
    • 子元素直接使用 “|” 隔开

      • 表示元素只能出现其中的任意一个

        <!ELEMENT person (男|女)>
        

4、DTD定义属性

  • 语法:

    <!ATTLIST 元素名称
    	属性名称 属性类型 属性的约束
    >
    
  • 属性值类型:

    • CDATA:表示属性的取值为普通的文本字符串

      <!ATTLIST birthday
         ID1 CDATA #REQUIRED
      >
      
    • ENUMERATED (DTD没有此关键字):表示枚举,只能从举列表中任选其一,如(鸡肉|牛肉猪肉|鱼肉)

      <!ATTLIST age
        ID2 (AA|BB|CC) #REQUIRED
      >
      
    • ID表示属性的取值不能重复,属性的值只能由字母,下划线开始,不能出现空白字符

      <!ATTLIST name
         ID3 ID #REQUIRED
      >
      
  • 属性约束设置说明

    • REQUIRED:表示该属性必须出现一次

    • MPLIED:表示该属性可有可无

    • FIXED:表示属性的取值为一个固定值。语法:

      #FIXED "固定值"
      
    • 直接值:表示属性的取值为该默认值

  • 定义属性源码示例

    <?xml version="1.0" encoding="utf-8" ?>
    <!DOCTYPE person[
            <!ELEMENT person (name+,age?,sex*,school,birthday)>
            <!ELEMENT name (#PCDATA)>
            <!ATTLIST name
                ID3 ID #REQUIRED 
            >
            <!ELEMENT age (#PCDATA)>
            <!ATTLIST age
                ID2 (AA|BB|CC) #REQUIRED
            >
    
            <!ELEMENT sex (#PCDATA)>
        	<!ATTLIST sex
                ID4 CDATA #FIXED "ABC"
            >
            
            <!ELEMENT school (#PCDATA)>
    		<!ATTLIST school
                ID5 CDATA "WWW"
            >
    
            <!ELEMENT birthday (#PCDATA)>
            <!ATTLIST birthday
                ID1 CDATA #REQUIRED
            >
    ]>
    <person>
        <name ID3="AA2">张三</name>
        <age ID2="AA">20</age>
        <sex ID4="ABC">222</sex>
        <school ID5="TTT">111</school>
        <birthday ID1="SSS">2022</birthday>
    </person>
    

5、实体的定义

  • 语法:

    <!ENTITY 实体名称 "实体的值">
    <!ENTITY TEST "HAHAHEHE">
    
  • 使用实体方法:&实体名称; 比如&TEST;

    <!ENTITY TEST "HELLOWORLD">
    <name ID3="BB">&TEST;</name>
    
  • 注意:定义实体需要写在内部dtd里面,如果写在外部的dtd里面,有某些浏览器下,内容得不到

六、xml解析之JAXP(SUN公司提供的API)

1、XML解析方式简介(最重要的内容)

  • xml是标记型文档

  • js使用dom解析标记型文档

    • 根据html的层级结构,在内存中分配一个树形结构,把html的标签,属性和文本都封装成对象
    • document对象、element对象、属性对象、文本对象、Node节点对象
  • xml的解析方式(技术): dom方式和sax方式

    • 画图分析使用dom和sax解析xml过程image-20240401150637721

    • dom解析和sax解析区别:

      • dom方式解析:
        • 根据xml的层级结构在内存中分配一个树形结构,把xml的标签,属性和文本都封装成对象
        • 缺点:如果文件过大,造成内存溢出
        • 优点:很方便实现增删改操作
      • sax方式解析
        • 采用事件驱动,边读边解析
          从上到下,一行一行的解析,解析到某一个对象,
          返回对象名称
        • 缺点:不能实现增删改操作
        • 优点:如果文件过大,不会造成内存溢出,方便实现查询操作

2、JAXP的API

想要解析xml,首先需要解析器

  • 不同的公司和组织提供了针对dom和sax方式的解析器,通过api方式提供

    • sun公司提供了针对dom和sax解析器 jaxp

    • dom4组织,针对dom和sax解析器 dom4j(实际开发中)

    • jdom组织,针对dom和sax解析器 jdom

  • Jaxp是javase的一部分

    • Jaxp解析器在jdk的javax. xml.parsers包里面

    • 四个类:分别是针对dom和sax解析使用的类

    • dom类

      • DocumentBuilder:解析器类

        这个类是一个抽象类,不能new

        此类的实例可以从 DocumentBuilderFactory.newDocumentBuilder()方法获取

        • 一个方法,可以解析xml parse ("xml路径")返回是 Document整个文档
        • 返回的document是一个接口,父节点是Node,如果在document里面找不到想要的方法,到Node里面去找
        • 在document里面方法
          • getElementsByTagName(string tagname)--这个方法可以得到标签
            • 返回集合NodeList
          • createElement (string tagName)--创创建标签
          • createTextNode (string data)--创建文本
          • appendchild (Node newchild)--把文本添加到标签下面
          • removechild (Node oldchild)--删除节点
          • getParentNode ()--获取父节点
          • getTextContent()--获取标签里的内容;
        • NodeList
          • getLength()--得到集合的长度
          • item (int index)--下标取到具体的值
            for (int i=0;i<list.getLength () ;i++){
            list.item (i)
            }
      • DocumentBuilderFactory:解析器工厂
        这个类也是一个抽象类,不能new
        newInstance()获取 DocumentBuilderFactory的实例。

    • sax类

      • SAXParser:解析器类
      • SAXParserFactory:解析器工厂

3、使用jaxp实现查询操作

  • 查询xml中所有的name元素的值

  • 步骤

    • 查询所有name元素的值

    • 1、创建解析器工厂

      DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
      
    • 2、根据解析器工厂创建解析器

      DocumentBuilder builder = builderFactory.newDocumentBuilder();
      
    • 3、解析xml返回document

      Document document = builder.parse("src/person.xml");
      
    • 4、得到所有的name元素

      NodeList nameList = document.getElementsByTagName("name");
      
    • 5、返回集合,遍历集合,得到每一个name元素

      for (int i = 0; i < nameList.getLength(); i++) {
          Node name1 = list.item(i); // 得到每一个name元素
          // 得到name元素里的每一个值
          String s = name1.getTextContent();
          System.out.println(s);
      }
      
  • 主程序代码

    import org.w3c.dom.Document;
    import org.w3c.dom.Node;
    import org.w3c.dom.NodeList;
    /**
     * 实现jaxp查询操作xml
     */
    public class TestJaxp {
        public static void main(String[] args) throws Exception {
            // 查询所有的 name 元素的值
            /**
             * 1.创建解析器工厂
             * 2.根据解析器工厂创建解析器
             * 3.解析xml返回Doucment对象
             * 4.得到所有的name元素的
             * 5.返回集合,遍历集合,得到每一个name元素
             */
            // 创建解析器工厂
            DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
            // 创建解析器
            DocumentBuilder builder = builderFactory.newDocumentBuilder();
            // 解析xml返回document对象
            Document document = builder.parse("src/person.xml");
            // 得到name元素
            NodeList nameList = document.getElementsByTagName("name");
            // 遍历集合
            for (int i = 0; i < nameList.getLength(); i++) {
                 Node name1 = nameList.item(i); // 得到每一个name元素
                // 得到name元素里的每一个值
                String s = name1.getTextContent();
                System.out.println(s);
            }
        }
    }
    

4、使用jaxp查询某一个节点里的值

  • 查询xml中第一个name元素的值

  • 步骤

    • 1、创建解析器工厂
    • 2、根据解析器工厂创建解析器
    • 3、解析xm1,返回document
    • 4、得到所有name元素
    • 5、使用返回集合,里面方法item,下标获取具体的元素
      • NodeList.item(下标):集合下标从0开始
    • 6、得到具体的值,使用getTextContent方法
  • 主程序代码

    public static void selectSin() throws Exception{
        /**
          * - 1、创建解析器工厂
          * - 2、根据解析器工厂创建解析器
          * - 3、解析xm1,返回document
          * - 4、得到所有name元素
          * - 5、使用返回集合,里面方法item,下标获取具体的元素
          * - NodeList.item(下标):集合下标从0开始
          * - 6、得到具体的值,使用getTextContent方法
          */
        // 创建解析器工厂
        DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
        // 根据解析器工厂创建解析器
        DocumentBuilder builder = builderFactory.newDocumentBuilder();
        // 解析xm1,返回document
        Document document = builder.parse("src/person.xml");
        // 使用返回集合,里面方法item,下标获取具体的元素
        NodeList nameList = document.getElementsByTagName("name");
        // 使用下标得到第一个元素
        Node name = nameList.item(0);
        String s = name.getTextContent();
        System.out.println(s);
    }
    

5、使用jaxp添加节点

  • 在第一个p1下面(末尾)添加

  • 步骤

    • 1、创建解析器工厂
    • 2、根据解析器工厂创建解析器
    • 3、解析xml,返回document
    • 4、得到第一个p1
    • 得到所有p1,使用item方法下标得到
    • 5、创建sex标签--createElement
    • 6、创建文本--createTextNode
    • 7、把文本添加到sex下面--appendchild
    • 8、把sex添加到第一个p1下面--appendchild
    • 9、回写xml,需要使用流回写
  • 主程序代码

    public static void addSex() throws Exception{
        /**
             * - 1、创建解析器工厂
             * - 2、根据解析器工厂创建解析器
             * - 3、解析xml,返回document
             * - 4、得到所有p1,使用item方法下标得到
             * - 得到第一个 p1
             * - 5、创建sex标签--createElement
             * - 6、创建文本--createTextNode
             * - 7、把文本添加到sex下面--appendchild
             * - 8、把sex添加到第一个p1下面--appendchild
             * - 9、回写xml,需要使用流回写
             *
             */
        // 创建解析器工厂
        DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
        // 根据解析器工厂创建解析器
        DocumentBuilder builder = builderFactory.newDocumentBuilder();
        // 解析xml,返回document
        Document document = builder.parse("src/person.xml");
        // 得到所有 p1
        NodeList p1_List = document.getElementsByTagName("p1");
        // 得到第一个 p1
        Node p1 =  p1_List.item(0);
        // 创建sex标签,使用createElement方法
        Element sex1 = document.createElement("sex");
        // 创建文本,使用createTextNode方法
        Text text1 = document.createTextNode("女");
        // 把文本添加到sex下面,使用appendchild方法
        sex1.appendChild(text1);
        // 把sex添加到第一个p1下面,使用appendchild方法
        p1.appendChild(sex1);
        // 回写xml,需要使用stream流回写
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.transform(new DOMSource(document),new StreamResult("src/person.xml"));
    
    }
    

6、使用jaxp修改节点内容

  • 修改第一个p1下面的内容为男

  • 步骤

    • 1、创建解析器工厂
    • 2、根据解析器工厂创建解析器
    • 3、解析xml,返回document
    • 4、得到sex
      • item方法
    • 5、修改sex里面的值
      • setTextContent方法
    • 6、回写xml
  • 主程序代码

    public static void modifySex() throws Exception{
        /**
             * - 1、创建解析器工厂
             * - 2、根据解析器工厂创建解析器
             * - 3、解析xml,返回document
             * - 4、得到sex - item方法
             * - 5、修改sex里面的值 - setTextContent方法
             * - 6、回写xml
             */
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        Document document = documentBuilder.parse("src/person.xml");
        // 得到sex找到第一个的sex - item方法
        Node sex1 =  document.getElementsByTagName("sex").item(0);
        // 修改sex里面的值 - setTextContent方法
        sex1.setTextContent("男");
        // 回写xml
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.transform(new DOMSource(document), new StreamResult("src/person.xml"));
    }
    

7、使用jaxp删除节点

  • 删除p1节点下的节点

  • 步骤

    • 1、创建解析器工厂
    • 2、根据解析器工厂创建解析器
    • 3、解析xml,返回document
    • 4、获取sex元素
    • 5、获取sex的父节点,使用getParentNode方法
    • 6、删除使用父节点删除,使用removeChild方法
    • 7、回写xml
  • 主程序代码

    public static void delSex() throws Exception{
        /**
             * - 1、创建解析器工厂
             * - 2、根据解析器工厂创建解析器
             * - 3、解析xml,返回document
             * - 4、获取sex元素
             * - 5、获取sex的父节点,使用getParentNode方法
             * - 6、删除使用父节点删除,使用removeChild方法
             * - 7、回写xml
             */
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        Document document = documentBuilder.parse("src/person.xml");
        // 获取sex元素,获得第一个sex
        Node sex1 = document.getElementsByTagName("sex").item(0);
        // 获取sex的父节点,使用getParentNode方法
        Node p1 = sex1.getParentNode();
        // 删除使用父节点删除,使用removeChild方法
        p1.removeChild(sex1);
        // 回写xml
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.transform(new DOMSource(document),new StreamResult("src/person.xml"));
    }
    

8、使用jaxp遍历节点

  • 把xml中的所有元素名称打印出来

  • 步骤

    • 1、创建解析器工厂
    • 2、根据解析器工厂创建解析器
    • 3、解析xml,返回document
    • 使用递归实现
    • 4、得到根节点
    • 5、得到根节点子节点
    • 6、得到根节点子节点的子节点
  • 注意:这里有两个难点,一个是递归方法的实现,还有一个就是剔除xml文件内的空格符号(做一个判断即可),得到相应的节点

  • 主程序代码

    public static void listElement() throws Exception{
        /**
             * - 1、创建解析器工厂
             * - 2、根据解析器工厂创建解析器
             * - 3、解析xml,返回document
             * - ==使用递归实现==
             * - 4、得到根节点
             * - 5、得到根节点子节点
             * - 6、得到根节点子节点的子节点
             */
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        Document document = documentBuilder.parse("src/person.xml");
        // 编写一个递归方法
        list(document);
    
    }
    public static void list(Node node) {
        // 判断是否有空格,没有空格就打印
        if(node.getNodeType() == Node.ELEMENT_NODE){
            System.out.println(node.getNodeName());
        }
        // 得到一层子节点
        NodeList list = node.getChildNodes();
        // 遍历 list
        for (int i = 0; i < list.getLength(); i++) {
            // 得到每一个节点
            Node node1 = list.item(i);
            // 继续得到子节点
            list(node1);
        }
    }
    

七、xml约束之一Schema

1、Schema简介

  • XML Schema也是一种用于定义和描述XML文档结构与内容的模式语言,其出现是为了克服DTD的局限性
  • XML Schema vS DTD:
    • XML Schema符合XML语法结构。
    • DOM、SAX等XML API很容易解析出XML Schema文档中的内容。
    • XML Schema对名称空间支持得非常好。
    • XML SchemakXML DTD支持更多的数据类型,并支持用户自定义新的数据类型。
    • XML Schema定义约束的能力非常强大,可以对XML实例文档作出细致的语义限制。
    • XML Schema不能像DTD样定义实体,比DTD更复杂,但XML Schema现在已是w3c组织的标准,它正逐步取代DTD。
  • 总结:
    • dtd语法:<!ELEMENT元素名称约束>
    • schema符合xml的语法,xml语句
    • 一个xml中可以有多个schema,多个schema使用名称空间区分(类似于java包名)
    • dtd里面有PCDATA类型,但是在schema里面可以支持更多的数据类型
      • 比如年龄只能是整数,在schema可以直接定义一个整数类型
    • schema语法更加复杂,schema目前不能替代dtd

2、Schema的开发过程

(1)Schema的一些概念

  • XML Schema文件自身就是一个XML文件,但它的扩展名通常为.xsd

  • 和XML文件一样,一个XML Schema文档也必须有一个根结点,但这个根结点的名称为Schema

  • 应用schema约束开发xml过程image-20240402161227091

  • 编写了一个XML Schema约束文档后,通常需要把这个文件中声明的元素绑定到一个URI地址上,这个URI地址叫
    namespace名称空间,以后XML文件就可以通过这个URI(即名称空间)引用绑定指定名称空间的元素。

(2)Schema的文档基本结构

  • 在W3C XML schema规范中规定:所有的Schema文档都使用作为其根元素

    <?xml version="1.0"?>
    <schema> 
    ...
    </schema>
    
  • 元素可以包含一些属性。一个XML schema声明看起来经常以如下的形式出现

    <schema xmlns="htp://www.w3.org/2001/XMl Schema
    targetNamespace="htip://www.itcast.cn/20140213" 
    elementFormDefault="qualified">
    
  • Schema开发过程流程图image-20240402163231679

  • 创建Schmea步骤

    • 创建一个schema文件后缀名是.xsd

      • 根节点
    • 在schema文件里面下的根节点写入如下属性

      • 属性

      • xmlns="http://www.w3.org/2001/XMLSchema"--表示当前xml文件是一个约束文件

      • targetNamespace="http://www.itcast.cn/20240402"--使用schema约束文件,直接通过这个地址引入约束文件

      • elementFormDefault="qualified"

        <?xml version="1.0" encoding="utf-8" ?>
        <schema xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.itcast.cn/20240402"
        elementFormDefault="qualified">
        
        </schema>
        
    • 步骤
      (1)看xml文件中有多少个元素,就创建多少个

      • 例如,person.xml文件里有三个标签person,name,age。那么在schema文件里根下添加
        3个

        <schema>
            <element name=""></element>
        	<element name=""></element>
        	<element name=""></element>
        </schema>
        

      (2)看简单元素和复杂元素

      • 复杂元素,需要在标签下添加标签,

        • :表示复杂类型
        • :表示有顺序
        • 格式如下:
        <element name="">
        	<complexType>
            	<sequence>
                	子元素
                </sequence>
             </complexType>
         </element>
        
      • 简单元素,需写在复杂元素里,例如person.xml文件里有三个标签person,name,age。name和age都是person的子元素,也是简单元素,相应的也可以规范其类型name为string类型,age为整型。写入schema文件格式如下:

        <element name="person">
                <complexType>
                    <sequence>
                        <element name="name" type="string"></element>
                        <element name="age" type="int"></element>
                    </sequence>
                </complexType>
        </element>
        

      (3)在被约束文件(xml文件)里面引入约束文件(Schema文件)

      <?xml version="1.0" encoding="UTF-8"?>
      <person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns="http://www.itcast.cn/20240402"
      xsi:schemaLocation="http://www.itcast.cn/20240402 textSchema.xsd">
          <name>张三</name>
          <age>20</age>
      </person>
      

3、XSD指示器

通过指示器,我们可以控制在文档中使用元素的方式。

有七种指示器:

Order 指示器:用于定义元素的顺序

  • All指示器

    • -- 规定子元素可以按照任意顺序出现,且每个子元素必须只出现一次

      <element name="person">
        <complexType>
          <all>
            <element name="child_name" type="string"/>
          <all>
        <complexType>
      <element>
      
  • Choice 指示器

    • -- 只能出现子元素内的其中一个

      <element name="person">
        <complexType>
          <choice> 
            <element name="child_name" type="string"/>
            <element name="mother_name" type="string"/>
          <choice> 
        <complexType>
      <element>
      
  • Sequence

    • -- 规定子元素必须按照特定的顺序出现

      <element name="person">
        <complexType>
          <sequence> 
            <element name="child_name" type="string"/>
            <element name="mother_name" type="string"/>
          <sequence> 
        <complexType>
      <element>
      

Occurrence 指示器:用于定义某个元素出现的频率

  • maxOccurs:规定某个元素可出现的最大次数

    <element name="person">
      <complexType>
        <sequence>
          <element name="child_name" type="xs:string" maxOccurs="10"/>
        <sequence>
      <complexType>
    <element>
    
  • minOccurs:可规定某个元素能够出现的最小次数

    <element name="person">
      <complexType>
        <sequence>
          <element name="child_name" type="xs:string" maxOccurs="10" minOccurs="0"/>
        <sequence>
      <complexType>
    <element>
    

Group 指示器:用于定义相关的数批元素

  • Group name:元素组

    • 元素组通过 group 声明进行定义:

      <group name="组名称">
        ...
      <group>
      
    • 必须在 group 声明内部定义一个 all、choice 或者 sequence 元素

      <group name="persongroup">
        <sequence>
          <element name="firstname" type="string"/>
          <element name="lastname" type="string"/>
        </sequence>
      </group>
      
    • group 定义完毕以后,就可以在另一个定义中引用它

      <group name="persongroup">
        <sequence>
          <element name="firstname" type="string"/>
          <element name="lastname" type="string"/>
          <element name="birthday" type="date"/>
        </sequence>
      </group>
      
      <element name="person" type="personinfo"/>
      
      <complexType name="personinfo">
        <sequence>
          <group ref="persongroup"/>
          <element name="country" type="string"/>
        </sequence>
      </complexType>
      
  • attributeGroup name:属性组

    • 属性组通过 attributeGroup 声明来进行定义:

      <xs:attributeGroup name="组名称">
        ...
      </xs:attributeGroup>
      
    • 定义了名为 "personattrgroup" 的一个属性组:

      <xs:attributeGroup name="personattrgroup">
        <xs:attribute name="firstname" type="xs:string"/>
        <xs:attribute name="birthday" type="xs:date"/>
      </xs:attributeGroup>
      
    • 定义完毕属性组之后,就可以在另一个定义中引用它:

      <xs:attributeGroup name="personattrgroup">
        <xs:attribute name="firstname" type="xs:string"/>
        <xs:attribute name="birthday" type="xs:date"/>
      </xs:attributeGroup>
      
      <xs:element name="person">
        <xs:complexType>
          <xs:attributeGroup ref="personattrgroup"/>
        </xs:complexType>
      </xs:element>
      

4、XSD定义属性(attribute)

简易元素无法拥有属性。假如某个元素拥有属性,它就会被当作某种复合类型。但是属性本身总是作为简易类型被声明的。

写在复杂元素里面

写在之前

声明属性的语法

<attribute name="id1" type="int" use="required"></attribute>
  • name:属性名称
  • type:属性类型int stirng
  • use:属性是否必须出现required

5、复杂的schema约束案例

两个约束文件名为company.xsd和department.xsd,一个被约束文件名为company.xml

  • company.xsd文件代码

    <schema xmlns="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://www.example.org/company"
            elementFormDefault="qualified">
        <element name="company">
            <complexType>
                <sequence>
                    <element name="employee">
                        <complexType>
                            <sequence>
                                <!-- 引用任何一个元素 -->
                                <any></any>
                                <!-- 员工名称 -->
                                <element name="name"></element>
                            </sequence>
                            <!-- 为employee元素添加属性 -->
                            <attribute name="age" type="int"></attribute>
                        </complexType>
                    </element>
                </sequence>
            </complexType>
        </element>
    </schema>
    
  • department.xsd文件代码

    <?xml version="1.0" encoding="UTF-8" ?>
    <schema xmlns="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://www.example.org/department"
            elementFormDefault="qualified">
        <!-- 部门名称 -->
        <element name="deptname" type="string"></element>
    </schema>
    
  • company.xml文件代码

    <?xml version="1.0" encoding="utf-8" ?>
    <!-- 数据文件 引用多个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">
            <!-- 部门名称 -->
            <dept:deptname>人力资源部</dept:deptname>
            <!-- 员工名称 -->
            <name>张三</name>
        </employee>
    </company>
    

6、sax的解析过程

  • 解析xml文件有两种技术:dom和sax

    • dom解析:根据xml的层级结构在内存中分配一个树形结构把xml中标签,属性,文本封装成对象

    • sax解析:事件驱动,边读边解析

      • 在javax. xml.parsers包里面

        • SAXParser类

          • 此类的实例可以从SAXParserFactory.newSAXParser()方法获得

          • parse (File f, DefaultHandler dh)

            • 两个参数
              第一个参数File f:xml的路径

              第二个参数DefaultHandler dh:事件处理器

        • SAXParserFactory类

          • 实例 newInstance() 方法得到
  • sax执行过程图解

    • 当解析到开始标签时候,自动执行startElement方法
    • 当解析到文本时候,自动执行characters方法
    • 当解析到结束标签时候,自动执行endElement方法
  • image-20240402203936886

7、使用sax方法查询xml(只能查询)

sax方式不能实现增删改操作,只能做查询操作打印出整个文档

  • 执行SAXParser类中public void parse(File f, DefaultHandler dh)方法;

    • 第一个参数xml路径
    • 第二个参数是事件处理器
  • 创建一个类,继承事件处理器的类,重写里面的三个方法

    • startElement(String uri, String localName, String qName, Attributes attributes)
    • characters(char[] ch, int start, int length)`
    • endElement(String uri, String localName, String qName)`
  • 获取到所有的name元素的值

    • 定义一个标志变量 flag= false
    • 判断开始方法是否是name元素,如果是name元素,把flag值设置成true
    • 如果flag值是true,在characters方法里面打印内容
    • 当执行到结束方法时候,把flag值设置成false
  • 获取第一个name元素的值

    • 定义一个成员变量idx=1

    • 在结束方法时候,idx+1 idx++

    • 想要打印出第一个name元素的值,

      • 在characters方法里面判断,

      • 判断flag=true并且idx==1,在打印内容

  • 主程序代码

    public class TextSax {
        public static void main(String[] args) throws Exception {
            /**
             * 1.创建解析器工厂
             * 2.创建解析器
             * 3.执行parse方法
             * 4.自己创建一个 MyDefault 类,继承DefaultHandler
             * 5.重写类里的三个方法
             */
            // 创建解析器工厂
            SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
            // 创建解析器
            SAXParser saxParser = saxParserFactory.newSAXParser();
            // 执行parse方法
            //saxParser.parse("src/person.xml",new MyDefault());
            saxParser.parse("src/person.xml",new MyDefault1());
        }
    }
    
    • 获取peroson.xml文件的所有内容

      // 获取peroson.xml文件的所有内容
      class MyDefault extends DefaultHandler{
          @Override
          public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
              System.out.print("<" + qName + ">");
          }
      
          @Override
          public void characters(char[] ch, int start, int length) throws SAXException {
              System.out.print(new String(ch,start,length));
          }
      
          @Override
          public void endElement(String uri, String localName, String qName) throws SAXException {
              System.out.print("</" + qName + ">");
          }
      }
      
    • 获取person.xml文件name元素内的值

      // 获取person.xml文件name元素内的值
      class MyDefault1 extends DefaultHandler{
          boolean flag = false;
          int ids = 1;
          @Override
          public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
             // 判断 qName是否为 name 元素,
              if ("name".equals(qName)){
                  flag = true;
              }
          }
      
          @Override
          public void characters(char[] ch, int start, int length) throws SAXException {
              // 当 flag值是true,表示解析到name元素
              if (flag == true && ids == 1){
                  System.out.println(new String(ch,start,length));
              }
          }
      
          @Override
          public void endElement(String uri, String localName, String qName) throws SAXException {
              // 把 flag设置成 false,表示name 元素结束
              if ("name".equals(qName)){
                  flag = false;
                  ids++;
              }
          }
      }
      

八、dom4j 解析 xml

1、dom4j简介

  • dom4j 是一个组织,针对xml解析,提供解析器dom4j。而dom4j不是javase的一部分,想要使用需要怎么做?

    • 导入dom4j提供jar包
    • 创建一个文件夹lib--复制dom4j的jar包到lib文件下,
    • 若是IDEA环境下,右键点击jar包,Add as Library 即可;若是Eclipse环境下,右键点击jar包,build path -- add to buildpath--看到jar包,变成奶瓶样子,表示导入成功
  • 得到document

    SAXReader reader = new SAXReader();
    Document document = reader.read(url);
    
  • document的父接口是Node

    • 如果在document里面找不到想要的方法,到Node接口里面去找
  • document里面的方法:getRootElement():获取根节点返回的是Element

  • Element也是一个接口,父接口也是Node

    • Element和Node里面方法
    • getParent():获取父节点
    • addElement():添加标签
    • element (qname):表示获取标签下面的第一个子标签
      • qname:标签的名称
    • elements (qname):获取标签下面是这个名称的所有子标签(一层)
      • qname:标签名称
    • elements():获取标签下面的所有一层子标签

2、使用dom4j查询xml

  • 查询person.xml文件内所有name元素里面的值

    • 1、创建解析器

    • 2、得到document

    • 3、得到根节点getRootElement()返回Element

    • 4、得到所有的p1标签

      • elements ( "p1")返回list集合
      • 遍历list得到每一个p1
    • 5、得到name

      • 在p1下面执行element ( "name")方法返回Element
    • 6、得到name里面的值

      • getText方法得到值
    • 方法代码

      public static void selectName() throws Exception {
          /**
               * - 1、创建解析器
               * - 2、得到document
               * - 3、得到根节点getRootElement()返回Element
               * - 4、得到所有的p1标签-elements ( "p1")返回list集合-遍历list得到每一个p1
               * - 5、得到name - 在p1下面执行element ( "name")方法返回Element
               * - 6、得到name里面的值 - getText方法得到值
               */
          // 创建解析器
          SAXReader saxReader = new SAXReader();
          // 得到document
          Document document = saxReader.read("src/person.xml");
          // 得到根节点
          Element root = document.getRootElement();
          // 得到所有的p1标签
          List<Element> p1_list = root.elements("p1");
          // 遍历 p1_list 列表
          for (Element element : p1_list) {
              // element是每一个p1元素
              // 获取 p1 标签下的 name 元素
              Element name1 = element.element("name");
              // 获取 name1 元素内的值
              String s = name1.getText();
              System.out.println(s);
          }
      }
      
  • 查询第一个name元素的值

    • 1、创建解析器

    • 2、得到document

    • 3、得到根节点

    • 4、得到第一个p1元素

      • element ( "p1")方法返回Element
    • 5、得到p1下面的name元素

      • element( "name")方法返回Element
    • 6、得到name元素里面的值

      • getText方法
    • 方法代码

      // 获取第一个name元素内的值
      public static void selectSin() throws Exception{
          SAXReader saxReader = new SAXReader();
          Document document = saxReader.read("src/person.xml");
          Element root = document.getRootElement();
          // 得到第一个p1元素
          Element p1 = root.element("p1");
          // 得到p1下面的name元素
          Element name1 = p1.element("name");
          // 得到name元素里面的值
          String s1 = name1.getText();
          System.out.println(s1);
      }
      
  • 获取第二个name元素的值

    • 1、创建解析器

    • 2、得到document

    • 3、得到根节点

    • 4、得到所有的p1

      • 回list集合
    • 5、遍历得到第二个p1

      • 使用list下标得到aet方法,集合的下标从0开始,想要得到第二个值,下标写1
    • 6、得到第二个p1下面的name

      • element ( "name")方法返回Element
    • 7、得到name的值

    • 方法代码

      // 获取第二个name元素内的值
      public static void selectSecond() throws Exception{
          SAXReader saxReader = new SAXReader();
          Document document = saxReader.read("src/person.xml");
          Element root = document.getRootElement();
          // 得到所有的p1
          List<Element> p1_list = root.elements("p1");
          // 遍历得到第二个p1
          Element p2 = p1_list.get(1);
          // 得到第二个p1下面的name
          Element name1 = p2.element("name");
          // 得到name的值
          String s1 = name1.getText();
          System.out.println(s1);
      }
      

3、使用dom4j实现添加操作

  • 在第一个p1标签末尾添加一个元素

  • 步骤

    • 1、创建解析器源

    • 2、得到document

    • 3、得到根节点

    • 4、获取到第一个p1

      • 使用element方法
    • 5、在p1下面添加元素

      • 在p1上面直接使用addElement("标签名称")方法返回一个Element
    • 6、在添加完成之后的元素下面添加文本

      • 在sex上直接使用setText("文本内容")方法
    • 7、回写xml

      • 使用OutputFormat格式化 一个值,然后将这个值传给new XMLWriter()方法。使用createPrettyPrint方法,表示一个漂亮的格式

        OutputFormat outputFormat = OutputFormat.createPrettyPrint();
        
      • 使用类XMLWriter类进行回写,需要new这个类,传递两个参数

        • 第一个参数是xml文件路径new Fileoutputstream("路径")

        • 第二个参数是格式化类的值

        • XMLWriter xmlWriter = new XMLWriter(new FileOutputStream("src/person.xml"),outputFormat);
          
      • 使用write()方法写入document;

      • 再关闭流。

    • 方法代码

      public static void addSex() throws Exception{
          SAXReader saxReader = new SAXReader();
          Document document = saxReader.read("src/person.xml");
          Element root = document.getRootElement();
          // 获取到第一个p1
          Element p1 = root.element("p1");
          // 在p1下面添加元素
          Element sex = p1.addElement("sex");
          // 在sex元素下面添加文本
          sex.setText("女");
          // 回写xml
          OutputFormat outputFormat = OutputFormat.createPrettyPrint();
          XMLWriter xmlWriter = new XMLWriter(new FileOutputStream("src/person.xml"),outputFormat);
          xmlWriter.write(document);
          xmlWriter.close();
      }
      

4、使用dom4j实现特定位置添加元素

  • 在第一个p1下面的age标签之前添加野鸡大学

  • 步骤

    • 1、创建解析器
    • 2、得到document
    • 3、得到根节点
    • 4、获取到第一个p1
    • 5、获取p1下面的所有的元素
      • elements ()方法返回list集合
      • 使用list里面的方法,在特定位置添加元素
      • 首先创建元素,在元素下面创建文本
        • 使用DocumentHelper类方法createElement创建标签
        • 把文本添加到标签下面使用setText("文本内容")方法
      • list集合里面的add(int index, Eelement)
        • 第一个参数是位置下标,从0开始
        • 第二个参数是要添加的元素
  • 6、回写xml

  • 方法代码

    // 在第一个p1下面的age标签之前添加<school>野鸡大学</schlool>
    public static void addAgeBefore() throws Exception{
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read("src/person.xml");
        Element root = document.getRootElement();
        // 获取到第一个p1
        Element p1 = root.element("p1");
        // 获取p1下面的所有的元素
        List<Element> p1_list = p1.elements();
        // 创建school元素
        Element school = DocumentHelper.createElement("school");
        // 在school元素下,创建文本
        school.setText("野鸡大学");
        // 在p1_list集合里面的add(int index, Eelement)添加school元素
        p1_list.add(1,school);
        // 回写xml
        OutputFormat format = OutputFormat.createPrettyPrint();
        XMLWriter xmlWriter = new XMLWriter(new FileOutputStream("src/person.xml"), format);
        xmlWriter.write(document);
        xmlWriter.close();
    }
    

5、dom4j里得封装方法

  • 可以将对得到document的操作回写xml的操作封装成方法也可以把传递的文件路径,封装成一个常量

    • 好处:可以提高开发速度,可以提交代码可维护性

      -比如想要修改文件路径(名称),这个时候只需要修改常量的值就可以了,其他代码不需要做任何改变。

  • 创建一个名为Utils包,再创建名为Dom4jUtils类进行封装操作

    • 传递的文件路径封装成一个常量

      public static final String PATH = "src/person.xml";
      
    • 对得到document的操作封装为Document getDocument(String path)方法

      public static Document getDocument(String path) {
          try {
              // 创建解析器
              SAXReader saxReader = new SAXReader();
              // 得到document
              Document document = saxReader.read(path);
              return document;
          } catch (Exception e) {
              e.printStackTrace();
          }
          return null;
          }
      }
      
    • 回写xml的操作封装为xmlWriters(String path, Document document)方法

      public static void xmlWriters(String path,Document document){
          try {
              OutputFormat format = OutputFormat.createPrettyPrint();
              XMLWriter xmlWriter = new XMLWriter(new FileOutputStream(path), format);
              xmlWriter.write(document);
              xmlWriter.close();
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
      
  • 总程序代码

    import org.dom4j.Document;
    import org.dom4j.io.OutputFormat;
    import org.dom4j.io.SAXReader;
    import org.dom4j.io.XMLWriter;
    public class Dom4jUtils {
    
        // 将传递的文件路径封装成常量
        public static final String PATH = "src/person.xml";
    
        // 返回都doucment
        public static Document getDocument(String path) {
            try {
                // 创建解析器
                SAXReader saxReader = new SAXReader();
                // 得到document
                Document document = saxReader.read(path);
                return document;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        // 回写xml的方法
        public static void xmlWriters(String path,Document document){
            try {
                OutputFormat format = OutputFormat.createPrettyPrint();
                XMLWriter xmlWriter = new XMLWriter(new FileOutputStream(path), format);
                xmlWriter.write(document);
                xmlWriter.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
  • 如何使用封装方法?

    • 因为都是static方法,直接 类名.方法名() 即可

      // 返回的document
      Document document = Dom4jUtils.getDocument(Dom4jUtils.PATH);
      // 回写xml
      Dom4jUtils.xmlWriters(Dom4jUtils.PATH,document);
      

6、使用dom4j实现修改节点操作

  • 修改第一个p1下面的age元素的值为30

  • 步骤

    • 1、得到document
    • 2、得到根节点,然后再得到第一个p1元素
    • 3、得到第一个p1下面的age
      • element ("")方法
    • 4、修改值是30
      • 使用setText ( "文本内容")方法
    • 5、回写xml
  • 方法代码

    // 修改第一个p1下面的age元素的值为<age>30</age>
    public static void modifyAge() throws Exception{
        // 得到document
        Document document = Dom4jUtils.getDocument(Dom4jUtils.PATH);
        // 得到根节点
        Element root = document.getRootElement();
        // 得到第一个p1元素
        Element p1 = root.element("p1");
        // 得到第一个p1元素下的age
        Element age = p1.element("age");
        // 修改值age为30,使用setText()方法
        age.setText("30");
        // 回写xml
        Dom4jUtils.xmlWriters(Dom4jUtils.PATH, document);
    }
    

7、使用dom4j实现删除节点操作

  • 删除第一个p1下面的野鸡大学元素

  • 步骤

    • 1、得到document
    • 2、得到根节点
    • 3、得到第一个业标签
    • 4、得到第一个p1下面的school元素
    • 5、删除(使用p1删除school)
      • 得到school的父节点
        • 第一种直接使用getRootElement()得到p1
        • 第二种使用方法getParent()方法得到
      • 删除操作
        • 在p1上面执行remove方法删除节点
    • 6、回写xml
  • 方法代码

    // 删除第一个p1下面的<school>野鸡大学</school>元素
    public static void delSch() {
        Document document = Dom4jUtils.getDocument(Dom4jUtils.PATH);
        Element root = document.getRootElement();
        // 得到第一个p1标签
        Element p1 = root.element("p1");
        // 得到第一个p1元素下的school元素
        Element school = p1.element("school");
        // 删除操作
        p1.remove(school);
        // 回写xml
        Dom4jUtils.xmlWriters(Dom4jUtils.PATH, document);
    }
    

8、使用dom4j获取属性操作

  • 获取第一个p1里面的属性id1的值

  • 步骤

    • 1、得到document
    • 2、得到根节点
    • 3、得到第一个p1元素
    • 4、得到p1里面的属性值
      • p1.attributevalue ( "id1");
      • 在p1上面执行这个方法,里面的参数是属性名称
  • 程序代码

    // 获取第一个p1里面的属性id1的值
    public static void getValues() throws Exception{
        Document document = Dom4jUtils.getDocument(Dom4jUtils.PATH);
        Element root = document.getRootElement();
        // 得到第一个p1元素
        Element p1 = root.element("p1");
        // 得到p1里面的属性值
        String id1 = p1.attributeValue("id1");
        System.out.println(id1);
    }
    

九、XPath

1、XPath简介

XPath 是一门在 XML 文档中查找信息的语言。XPath 用于在 XML 文档中通过元素和属性进行导航。

一、什么是 XPath?

  • XPath 使用路径表达式在 XML 文档中进行导航
  • XPath 包含一个标准函数库
  • XPath 是 XSLT 中的主要元素
  • XPath 是一个 W3C 标准

二、XPath 路径表达式

XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。

三、XPath 标准函数

XPath 含有超过 100 个内建的函数。这些函数用于字符串值、数值、日期和时间比较、节点和 QName 处理、序列处理、逻辑值等等。

2、XPath语法

XPath 使用路径表达式来选取 XML 文档中的节点或节点集。节点是通过沿着路径 (path) 或者步 (steps) 来选取的。

以下面的xml文件为例

<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
    <book>
        <title lang="eng">Harry Potter</title>
        <price>29.99</price>
    </book>
    <book>
        <title lang="eng">Learning XML</title>
        <price>39.95</price>
    </book>
</bookstore>

1.选取节点

  • XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。

  • 最有用的路径表达式:

    表达式 描述
    nodename 选取此节点的所有子节点。
    / 从根节点选取。
    // 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
    . 选取当前节点。
    .. 选取当前节点的父节点。
    @ 选取属性。
  • 示例

    列出了一些路径表达式以及表达式的结果:

    路径表达式 结果
    bookstore 选取 bookstore 元素的所有子节点。
    /bookstore 选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
    bookstore/book 选取属于 bookstore 的子元素的所有 book 元素。
    //book 选取所有 book 子元素,而不管它们在文档中的位置。
    bookstore//book 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
    //@lang 选取名为 lang 的所有属性。

2.谓语(Predicates)

谓语用来查找某个特定的节点或者包含某个指定的值的节点。

谓语被嵌在方括号中。

  • 示例

    路径表达式 结果
    /bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素。
    /bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素。
    /bookstore/book[last()-1] 选取属于 bookstore 子元素的倒数第二个 book 元素。
    /bookstore/book[position()❤️] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
    //title[@lang] 选取所有拥有名为 lang 的属性的 title 元素。
    //title[@lang='eng'] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。
    /bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
    /bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。

4.通配符

XPath 通配符可用来选取未知的 XML 元素。

通配符 描述
* 匹配任何元素节点。
@* 匹配任何属性节点。
node() 匹配任何类型的节点。
  • 示例

    路径表达式 结果
    /bookstore/* 选取 bookstore 元素的所有子元素。
    //* 选取文档中的所有元素。
    //title[@*] 选取所有带有属性的 title 元素。

5.选取若干路径

通过在路径表达式中使用 “|” 运算符,您可以选取若干个路径。

  • 示例

    路径表达式 结果
    //book/title | //book/price 选取 book 元素的所有 title 和 price 元素。
    //title | //price 选取文档中的所有 title 和 price 元素。
    /bookstore/book/title | //price 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。

3、使用dom4j支持xpath具体操作

  • 默认的情况下, dom4j不支持xpath
  • 如果想要在dom4j里面是有xpath
    • 第一步需要,引入支持xpath的jar包,使用jaxen-1.1-beta-6.jar
    • 需要把jar包导入到项目中
  • 在dom4j里面提供了两个方法,用来支持xpath
    • selectNodes("xpath表达式") -- 获取多个节点
    • selectsingleNode ( "xpath表达式") -- 获取一个节点

1.使用xpath实现:查询xml中所有name元素的值

  • 所有name元素的xpath表示://name

  • 使用selectNodes ( "//name");

  • 步骤

    • 1、得到document
    • 2、直接使用selectNodes (" //name")方法得到所有的name元素
  • 方法代码

    // 查询xml中所有name元素的值
    public static void xpath_SelectName() throws Exception{
        // 得到document
        Document document = Dom4jUtils.getDocument(Dom4jUtils.PATH);
        // 直接使用selectNodes (" //name")方法得到所有的name元素
        List<Node> nameList = document.selectNodes("//name");
        // 遍历 nameList列表,获取每一个name元素内的值
        for (Node node:nameList) {
            // node是每一个name元素
            // 得到name元素里面的值
            String s = node.getText();
            System.out.println(s);
        }
    }
    

2.使用xpath查询xml中第一个p1下的name元素内的值

  • XPath查询第一个p1下name元素内的值的语法:

    • //p1[@id1='AAAA']/name
      
  • 在java中类使用到

    selectSingleNode ( "//p1[@id1='AAAA']/name")
    
  • 步骤和代码

    • 1、得到document

    • 2、直接使用selectSingleNode方法实现

      • xpath : //p1[@id1= 'AAAA' ] /name
    • 得到document

      • Document document = Dom4jutils.getDocument (Dom4jutils.PATH);
        
    • 直接使用selectSingleNode方法,获得name元素

      • Node name1 = document.selectSingleNode("//p1[@id1='AAAA' ]/name"); 
        
    • 得到name元素里面的值

      • string s1 = name1.getText();
        system.out.println(s1);
        
  • 方法代码

    // 查询xml中第一个p1下的name元素内的值
    public static void xpath_SelectFirstName() throws Exception{  
             * - 1、得到document
             * - 2、直接使用selectsingleNode方法实现
             *   - xpath : //p1[@id1= 'AAAA' ]/name
             *  - 直接使用selectsingleNode方法,获得name元素
             */
        // 得到document
        Document document = Dom4jUtils.getDocument(Dom4jUtils.PATH);
        // 直接使用selectsingleNode方法实现
        Node name = document.selectSingleNode("//p1[@id1= 'AAAA' ]/name");
        // 得到name元素里的值
        String s = name.getText();
        System.out.println(s);
    }
    
posted @ 2024-05-06 02:25  粤先生  阅读(72)  评论(0)    收藏  举报