XML

XML 简介 (w3school.com.cn)open in new window

1.概述

XML(Extensible Markup Language 可扩展标记语言)

XML的作用

1、用来保存数据,而且这些数据具有自我描述性

2、它还可以做为项目或者模块的配置文件

3、还可以做为网络传输数据的格式(现在 JSON 为主)

2.XML语法

  1. 文档声明。

  2. 元素(标签)

  3. xml 属性

  4. xml 注释

  5. 文本区域(CDATA 区)

3.1 文档声明

注意:<?xml 要连在一起写,否则会有报错

<?xml version="1.0" encoding="UTF-8"?> 
<!-- xml 声明 version 是版本的意思 encoding 是编码 --> 
<books> <!-- 这是 xml 注释 -->
    <book id="SN123123413241"> 
        <!-- book 标签描述一本图书 id 属性描述 的是图书 的编号 --> 
        <name>java 编程思想</name> <!-- name 标签描述 的是图书 的信息 --> 
        <author>华仔</author> <!-- author 单词是作者的意思 ,描述图书作者 --> 
        <price>9.9</price> <!-- price 单词是价格,描述的是图书 的价格 --> 
    </book> 
    <book id="SN12341235123"> <!-- book 标签描述一本图书 id 属性描述 的是图书 的编号 --> 
        <name>葵花宝典</name> <!-- name 标签描述 的是图书 的信息 --> 
        <author>班长</author> <!-- author 单词是作者的意思 ,描述图书作者 --> 
        <price>5.5</price>
    <!-- price 单词是价格,描述的是图书 的价格 --> 
    </book> 
</books>

3.2 注释

html 和 XML 注释 一样 : <!-- html 注释 -->

3.3 元素(标签)

  1. 什么是 xml 元素

2022-09-21_110350

  1. XML 命名规则

    1. 名称可以含字母、数字以及其他的字符
    2. 名称不能以数字或者标点符号开始
    3. 名称不能包含空格
  2. xml 中的元素(标签)也分成单标签和双标签

    2022-09-21_110657

3.4 xml 属性

xml 的标签属性和 html 的标签属性是非常类似的,属性可以提供元素的额外信息。一个标签上可以书写多个属性。每个属性的值必须在引号内。规则和标签的书写规则一致。

2022-09-21_110850

3.5 语法规则

  1. 所有 XML 元素都须有关闭标签(也就是闭合)

  2. XML 标签对大小写敏感

  3. XML 必须正确地嵌套

  4. XML 文档必须有根元素

    2022-09-21_111037

  5. XML 的属性值须加引号

  6. XML 中的特殊字符

    2022-09-21_111133

  7. 文本区域(CDATA区)

    CDATA 语法可以告诉 xml 解析器,我 CDATA 里的文本内容,只是纯文本,不需要 xml 语法解析。CDATA 格式: <![CDATA[ 这里可以把你输入的字符原样显示,不会解析 xml ]]>

    2022-09-21_111341

3.6 约束

规定xml文档的书写规则

  • 作为框架的使用者(程序员):

    1. 能够在xml中引入约束文档
    2. 能够简单的读懂约束文档
  • 分类:

    1. DTD:一种简单的约束技术
    2. Schema:一种复杂的约束技术
  • DTD: * 引入dtd文档到xml文档中 * 内部dtd:将约束规则定义在xml文档中 * 外部dtd:将约束的规则定义在外部的dtd文件中 * 本地:<!DOCTYPE 根标签名 SYSTEM "dtd文件的位置"> * 网络:<!DOCTYPE 根标签名 PUBLIC "dtd文件名字" "dtd文件的位置URL">

  • Schema:

    • 引入:
      • 1.填写xml文档的根元素
      • 2.引入xsi前缀. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      • 3.引入xsd文件命名空间. xsi:schemaLocation="http://www.itcast.cn/xml student.xsd"
      • 4.为每一个xsd约束声明一个前缀,作为标识 xmlns="http://www.itcast.cn/xml"
    <?xml version="1.0" encoding="UTF-8" ?>
    <students   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    			xmlns="http://www.itcast.cn/xml"
    			xsi:schemaLocation="http://www.itcast.cn/xml  student.xsd"
    >
    	<student number="heima_0001">
    		<name>tom</name>
    		<age>18</age>
    		<sex>male</sex>
    	</student>
    
    </students>
    

3.XML解析技术

xml 可扩展的标记语言。 不管是 html 文件还是 xml 文件它们都是标记型文档,都可以使用 w3c 组织制定的 dom 技术来解析。2022-09-21_111605

document 对象表示的是整个文档(可以是 html 文档,也可以是 xml 文档)

早期 JDK 为我们提供了两种 xml 解析技术 DOM 和 SAX (已经过时,但我们需要知道这两种技术

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

dom 解析技术是 W3C 组织制定的,而所有的编程语言都对这个解析技术使用了自己语言的特点进行实现。 Java 对 dom 技术解析标记也做了实现。

sun 公司在 JDK5 版本对 dom 解析技术进行升级:SAX( Simple API for XML )

SAX 解析,它跟 W3C 制定的解析不太一样。它是以类似事件机制通过回调告诉用户当前正在解析的内容。 它是一行一行的读取 xml 文件进行解析的。不会创建大量的 dom 对象。 所以它在解析 xml 的时候,在内存的使用上。和性能上。都优于 Dom 解析。

第三方的解析:

  • jdom 在 dom 基础上进行了封装 、
  • dom4j 又对 jdom 进行了封装。
  • pull 主要用在 Android 手机开发,是在跟 sax 非常类似都是事件机制解析 xml 文件。
  • Jsoup:jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。

Dom4j 和 Jsoup 是第三方的解析技术。我们需要使用第三方给我们提供好的类库才可以解析 xml 文件

3.1 dom4j

由于 dom4j 它不是 sun 公司的技术,而属于第三方公司的技术,我们需要使用 dom4j 就需要到 dom4j 官网下载 dom4j 的 jar 包。

3.1.1 Dom4j 类库的使用

2022-09-21_112543

解压后:

2022-09-21_112556

3.1.2 dom4j 目录的介绍

  1. docs是文档目录

2022-09-21_112711

  • 如何查看 Dom4j 的文档

2022-09-21_112755

  • Dom4j 快速入门

2022-09-21_112828

  1. lib 目录

2022-09-21_113007

  1. src 目录是第三方类库的源码目录

2022-09-21_113049

3.1.3 dom4j 编程步骤

第一步: 先加载 xml 文件创建 Document 对象

第二步:通过 Document 对象拿到根元素对象

第三步:通过根元素.elelemts(标签名); 可以返回一个集合,这个集合里放着。所有你指定的标签名的元素对象

第四步:找到你想要修改、删除的子元素,进行相应在的操作

第五步,保存到硬盘上

3.1.4 获取 document 对象

创建一个 lib 目录,并添加 dom4j 的 jar 包。并添加到类路径。

2022-09-21_113254

需要解析的 books.xml 文件内容

<?xml version="1.0" encoding="UTF-8"?> 
<books> 
    <book sn="SN12341232"> 
        <name>辟邪剑谱</name> 
        <price>9.9</price>
        <author>班主任</author> 
    </book> 
    <book sn="SN12341231"> 
        <name>葵花宝典</name> 
        <price>99.99</price> 
        <author>班长</author> 
    </book> 
</books>

解析获取 Document 对象的代码。第一步,先创建 SaxReader 对象。这个对象,用于读取 xml 文件,并创建 Document

/** dom4j 获取 Documet 对象 */ 
@Test 
public void getDocument() throws DocumentException { 
    // 要创建一个 Document 对象,需要我们先创建一个 SAXReader 对象 
    SAXReader reader = new SAXReader(); 
    // 这个对象用于读取 xml 文件,然后返回一个 Document。 
    Document document = reader.read("src/books.xml"); 
    // 打印到控制台,看看是否创建成功 
    System.out.println(document); }

3.1.5 遍历标签获取所有标签中的内容

需要分四步操作:

第一步,通过创建 SAXReader 对象。来读取 xml 文件,获取 Document 对象

第二步,通过 Document 对象。拿到 XML 的根元素对象

第三步,通过根元素对象。获取所有的 book 标签对象

第四步,遍历每个 book 标签对象。然后获取到 book 标签对象内的每一个元素,再通过 getText() 方法拿到起始标签和结束标签之间的文本内容

/**
     * 读取 xml 文件中的内容
     */
    @Test
    public void readXML() throws DocumentException {

        // 第一步,通过创建 SAXReader 对象。来读取 xml 文件,获取 Document 对象
        SAXReader reader = new SAXReader();
        Document document = reader.read("src/books.xml");
        // 第二步,通过 Document 对象。拿到 XML 的根元素对象
        Element root = document.getRootElement();
        // 打印测试 // Element.asXML() 它将当前元素转换成为 String 对象
        System.out.println(root.asXML());
        // 第三步,通过根元素对象。获取所有的 book 标签对象 Element.elements(标签名)它可以拿到当前元素下的指定的子元素的集合
        List<Element> books = root.elements("book");
        // 第四小,遍历每个 book 标签对象。然后获取到 book 标签对象内的每一个元素,
        for (Element book : books) {
            // 测试
            System.out.println(book.asXML());
            // 拿到 book 下面的 name 元素对象
            Element nameElement = book.element("name");
            // 拿到 book 下面的 price 元素对象
            Element priceElement = book.element("price");
            // 拿到 book 下面的 author 元素对象
            Element authorElement = book.element("author");
            // 再通过 getText() 方法拿到起始标签和结束标签之间的文本内容
            System.out.println("书名" + nameElement.getText() + " , 价格:" + priceElement.getText() + ", 作者:" + authorElement.getText());
        }
    }

3.2 Jsoup

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

3.2.1 Jsoup 编程步骤

步骤:

  1. 导入jar包
  2. 获取Document对象
  3. 获取对应的标签Element对象
  4. 获取数据
public class JsoupDemo1 {
    public static void main(String[] args) throws IOException {
        //2.获取Document对象,根据xml文档获取
        //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);
    }
}

对象的使用: 1. Jsoup:工具类,可以解析html或xml文档,返回Document * parse:解析html或xml文档,返回Document * parse(File in, String charsetName):解析xml或html文件的。 * parse(String html):解析xml或html字符串 * parse(URL url, int timeoutMillis):通过网络路径获取指定的html或xml的文档对象 2. Document:文档对象。代表内存中的dom树

  • 获取Element对象
    • getElementById(String id):根据id属性值获取唯一的element对象
    • getElementsByTag(String tagName):根据标签名称获取元素对象集合
    • getElementsByAttribute(String key):根据属性名称获取元素对象集合
    • getElementsByAttributeValue(String key, String value):根据对应的属性名和属性值获取元素对象集合
    • select(String cssQuery) 3. Elements:元素Element对象的集合。可以当做 ArrayList<Element>来使用 4. Element:元素对象
  1. 获取子元素对象
* getElementById(String id):根据id属性值获取唯一的element对象
* getElementsByTag(String tagName):根据标签名称获取元素对象集合
* getElementsByAttribute(String key):根据属性名称获取元素对象集合
* getElementsByAttributeValue(String key, String value):根据对应的属性名和属性值获取元素对象集合
  1. 获取属性值
    • String attr(String key):根据属性名称获取属性值
  2. 获取文本内容
    • String text():获取文本内容
    • String html():获取标签体的所有内容(包括字标签的字符串内容) 5. Node:节点对象 1. 是Document和Element的父类

3.2.1 获取Document

3.2.1.1 从字符串生成Document

关键方法:Jsoup.parse 代码

 /**
     * 将字符串转化为Document
     * 
     * html: https://jsoup.org/cookbook/input/parse-document-from-string
     */
    public void parseDocumentFromString() {
        String html = "<html><head><title>First parse</title></head>"
                + "<body><p>Parsed HTML into a doc.</p></body></html>";
        Document doc = Jsoup.parse(html);
        logger.info("parseDocumentFromString content={}", doc);
    }
3.2.1.2 从网页生成Document

关键方法: Jsoup.connect 代码

/**
     * 从网络上加载网页并转化为Document
     * 
     * html: https://jsoup.org/cookbook/input/load-document-from-url
     */
    public void loadDocumentFromURL() {
        Document doc;
        try {
            doc = Jsoup.connect("https://www.baidu.com/").get();
            // 从document中获取title值
            String title = doc.title();
            logger.info("LoadDocumentFromURL title={}", title);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

3.2.1.3 从本地文件生成Document
/**
     * 从本地加载文件并转化为Document
     * 
     * html: https://jsoup.org/cookbook/input/load-document-from-file
     */
    public void loadDocumentFromFile() {
        URL fileUrl = LoadParseDocument.class.getResource("/com/hry/tool/jsoup/doc/loadDocumentFromFile.html");
        File input = new File(fileUrl.getFile());
        try {
            /* The baseUri parameter is used by the parser to resolve relative URLs in the document 
             * before a <base href> element is found. If that's not a concern for you, 
             * you can pass an empty string instead.
             */
            Document doc = Jsoup.parse(input, "UTF-8", "https://www.baidu.com/");
            logger.info("LoadDocumentFromFile content={}", doc);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

3.2.2 获取Element

下面就可以对这个document进行操作,操作的主要单位是Element,下面介绍如何选取elment及获取elment的内容。

  1. 获取Element,获取的方式有二种:
    1. DOM;
    2. 通过css或类似jquery的selector语法
3.2.2.1 DOM
    /**
     *  调用document的类似DOM的方法获取Element
     * 
     * html: https://jsoup.org/cookbook/extracting-data/dom-navigation
     * @throws IOException
     */
    public void extractDataByDOM() throws IOException{
        Document doc = Jsoup.connect("https://www.baidu.com/").get();
        Element lg = doc.getElementById("lg");
        logger.info("getElementById lg = {}", lg);
        Elements links = doc.getElementsByTag("a");
        for (Element link : links) {
          String linkHref = link.attr("href");
          String linkText = link.text();
          logger.info("linkHref={}, linkText={}",linkHref, linkText);
        }
    }

3.2.2.2 selector语法

测试文件: extractDataByCSSOrJqueryLikeSelectorSyntax.html

<html>
<header>
    <h3 class="r">h3<a href="/test/h3"></a></b></h3>
</header>
<body>
.. body
<div>
    <a href="/test" >test</a>
    <a href="/test2" >test2</a>
</div>
<div>
    <img alt="test" src="/image/a.png" />
    <img alt="test2" src="/image/b.png" />
</div>
<div>
    <div class="masthead">masthead</div>
</div>
</body>
</html>

代码:

  /**
     * 通过css或类似jquery的selector语法
     * 
     * html: https://jsoup.org/cookbook/extracting-data/selector-syntax
     * @throws IOException 
     */
    public void extractDataByCSSOrJqueryLikeSelectorSyntax() throws IOException{
        URL fileUrl = LoadParseDocument.class.getResource("/com/hry/tool/jsoup/extractingdata/extractDataByCSSOrJqueryLikeSelectorSyntax.html");
        File input = new File(fileUrl.getFile());
        Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");

        // 获取所有的a节点
        Elements links = doc.select("a[href]"); 
        logger.info("links = {}", links);

        // 获取img的src以.png结果结尾
        Elements pngs = doc.select("img[src$=.png]");
        logger.info("pngs = {}", pngs);

        // 获取class=masthead的div节点
        Element masthead = doc.select("div.masthead").first();
        logger.info("masthead = {}", masthead);

        // 获取class=r的h3节点下面的a节点
        Elements resultLinks = doc.select("h3.r > a"); // direct a after h3
        logger.info("resultLinks = {}", resultLinks);
    }

3.2.3 获取节点属性、文本、html

  /**
     * 获取节点属性、文本、html
     * 
     * html: https://jsoup.org/cookbook/extracting-data/attributes-text-html
     */
    public void extractAttributesTextAndHTML(){
        String html = "<p>An <a href='/abc'><b>example</b></a> link.</p>";
        // 指定baseUri的值,在使用abs:attr会使用到
        Document doc = Jsoup.parse(html, "http://example.com/");
        Element link = doc.select("a").first();

        /**
         * text获取所有子节点的文本并组合在一一起
         * 如:HTML <p>Hello <b>there</b> now! </p>, --》 调用p.text() --》输出: "Hello there now!"
         */
        String text = doc.body().text(); // "An example link"
        logger.info("text={}", text);

        // 获取link的href属性值
        String linkHref = link.attr("href"); // /abc
        // 在href前面加上abs,会在现有的href(如/abc)的前面别上Jsoup.parse(html, "http://example.com/")里指定的baseUri值
        String absLinkHref = link.attr("abs:href"); // http://example.com/abc
        logger.info("linkHref={}, absLinkHref={}",linkHref, absLinkHref);

        // 获取link里所有字节点的内容组合在一起
        String linkText = link.text(); // "example""
        logger.info("linkText={}", linkText);

        // 获取本节点所有HTML文本信息
        String linkOuterH = link.outerHtml(); // "<a href="http://example.com"><b>example</b></a>"
        logger.info("linkOuterH={}", linkOuterH);

        // 获取本节点字节点的html文本信息
        String linkInnerH = link.html(); // "<b>example</b>"
        logger.info("linkInnerH={}", linkInnerH);
    }
}

3.2.4 设置节点值

公共测试文件

<html>
<body>
.. body
<div></div>
<span>One</span>
</body>
</html>

从本地文件生成Document文本

  private Document getDoucment() throws IOException{
        URL fileUrl = LoadParseDocument.class.getResource("/com/hry/tool/jsoup/modifyingdata/modifyingData.html");
        File input = new File(fileUrl.getFile());
        Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");
        return doc;
    }
3.2.4.1 设置节点HTML的值

设置节点的html内容:

  1. Element.html:使用新的HTML替换旧的值
  2. Element.prepend:将新html添加到指定节点内部的最前面
  3. Element.append:将新html添加到指定节点内部的最后面
  4. Element.wrap:将指定节点封装到html最里面
/**
 * 设置节点的html内容:
 *  1. html:使用新的HTML替换旧的值
 *  2. prepend:将新html添加到指定节点内部的最前面
 *  3. append:将新html添加到指定节点内部的最后面
 *  4. wrap:将指定节点封装到html最里面
 *  
 *  html: https://jsoup.org/cookbook/modifying-data/set-html
 * @throws IOException
 */
public void setHTMLofAnElement() throws IOException{
    Document doc = getDoucment();

    // 获取div节点
    Element div = doc.select("div").first(); // <div></div>
    // 使用新的HTML替换div旧的html值
    div.html("<p>lorem ipsum</p>"); // <div><p>lorem ipsum</p></div>
    // 将新html添加到div内部的最前面
    div.prepend("<p>First</p>"); // <div><p>First</p><p>lorem ipsum</p></div>
    // 将新html添加到div内部的最后面
    div.append("<p>Last</p>");  // now: <div><p>First</p><p>lorem ipsum</p><p>Last</p></div>
    logger.info("now div={}", div);

    Element span = doc.select("span").first(); // <span>One</span>
    // 将span节点封装到html最里面 --》 <li><a href="http://example.com"><span>One</span></a></li>
    span.wrap("<li><a href='http://example.com/'></a></li>");

    logger.info("doc={}", doc);
}
3.2.4.2 设置节点内容的值

修改节点内容

  1. Element.text: 完全替换内容
  2. Element.prepend:在节点的内容最前面加内容
  3. Element.append:在节点的内容最后面加内容
  /**
     * 修改节点内容
     * html: https://jsoup.org/cookbook/modifying-data/set-text
     * @throws IOException
     */
    public void setTextContentofAnElement() throws IOException{
        Document doc = getDoucment();

        Element div = doc.select("div").first(); // <div></div>
        // 替换div里的内容 输出 -->  <div>five four</div>
        div.text("five four"); 
        logger.info("text = {}", div);

        // 在div的内容最前面加内容  输出 --> <div> First five four</div>
        div.prepend("First ");
        logger.info("prepend = {}", div);

        // 在div的内容最后面加内容  输出 --> <div>First five four Last</div>
        div.append(" Last");
        logger.info("append = {}", div);
    }

3.2.4.3 置节点属性的值

设置节点的属性和class值

  1. element.attr: 设置属性
  2. element.addClass: 设置class
/**
     * 设置节点的属性和class值
     *  1. 设置属性
     *  2. 设置class
     * 
     * html:https://jsoup.org/cookbook/modifying-data/set-attributes
     * @throws IOException
     */
    public void setAttributeValues() throws IOException{
        Document doc = getDoucment();
        Element div = doc.select("div").first(); 
        // 在div节点上增加属性和其值:输出 --> <div title="nofollow"></div>
        div.attr("title", "nofollow");
        logger.info("div={}", div);
        // 在div节点上增加class和其值:输出 --> <div title="nofollow" class="round-box"></div>
        div.addClass("round-box");
        logger.info("div={}", div);
    }

参考:Jsoup使用总结 - 掘金 (juejin.cn)open in new window