likes
comments
collection
share

【Java】基础知识-JAXP解析XML(一)

作者站长头像
站长
· 阅读数 23

1 XML概述

1.1 XML简介

【Java】基础知识-JAXP解析XML(一)

定义

XML 指可扩展标记语言(eXtensibleMarkupLanguage)。可扩展标记语言(英语:Extensible Markup Language,简称:XML)是一种标记语言,是从标准通用标记语言(SGML)中简化修改出来的。它主要用到的有可扩展标记语言、可扩展样式语言(XSL)、XBRL和XPath等。

用途

传输和存储数据。

  • 数据传输:例如QQ聊天,微信聊天,获取天气数据。
  • 显示数据之间的关系:行政区划之间的层级关系
  • 配置文件:数据库连接经常会被配置在XML文件中

特点

与开发语言的操作系统无关,可跨平台实现操作系统间的通信。

  • 可标记。同Html一样,都有自己的标签
  • 可扩展。比Html多出来可以自行定义的标签

具象描述

可扩展标记语言;很像HTML的标记语言;设计宗旨是传输数据,而不是显示数据;XML 标签没有被预定义;可以自定义标签对;被设计为具有自我描述性;W3C 的推荐标准。

1.2 语法标记

「后缀名」

XML文件的后缀名是.xml。

「文档声明」

必须在文件的第一行标记

「属性」

  • version:xml的版本 1.0
  • encoding:xml编码 gbk utf-8 iso8859-1(不包含中文)
  • standalone:是否需要依赖其他文件yes/no

可以参考【Python】操作XML

1.3 标签

「标签定义」

在XML中,标签可以自行定义名称,标签都是成对出现,有开始就要有结束。

例如:章三, 就是一个标签对。在Html中的标签对也符合这种规范,例如

有些没有成对出现的,例如 <script />

这是一种简写。从这里可看出来有三种类型:

  • 成对标签不包含标签体 <person></person>

  • 成对标签包含标签体 <age>20</age>

「标签嵌套」

标签可以嵌套,必须要合理嵌套。

「标签语言格式」

可以是任意可解析的语言,例如英文,中文等。

「英文」

<Student>章三</Student>

「中文」

<城市>广州市</城市>

1.4 属性

<person class="1" sex="男">
  <name>张三</name>
</person>

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

(2)属性名称不能相同

(3) 属性名称和属性值之间使用=,属性值使用引号括起来(可以单引号也可以双引号)

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

1.5 注释

编写注释的时候需要注意以下几点:

1、写法:

2、不能嵌套编写,例如 -->

3、不能在第一行编写,可以参考 1.2

1.6 特殊字符

特殊转义符

转义后转义前解释描述
&lt;< less than
&gt;> greater than
&amp;&ampersand
&apos;' apostrophe
&quot;"quotation mark

1.7 XML的CDATA区域

CDATA区段中的文本会被解析器忽略,其他的都会被XML解析器解析。

「作用」

1、解决多个字符都需要转义的操作

2、CDATA区里面,不需要转义

「格式」

<![CDATA[" 编写的内容 "]]>

1.8 PI指令和约束

「PI指令」

主要用于样式。

1.9 DTD约束

DTD 约束」

1、编写DTD文件

创建一个.dtd文件

2、编写约束

编写约束使用的标签是

3、引入约束文件

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

dtd引入方式:内部引入、外部引入、网络引入。上述实验就是外部引入的一个实例。

  • 外部引入
<!DOCTYPE  根元素名称  SYSTEM “dtd路径”>
  • 内部引入
< !DOCTYPE 根元素名称[
<!ELEYENT person (name , age )>
<!ELEMENT name (#PCDATA)
<!ELEMENT age (#PCDATA )>
]>
  • 网络引入
<!DOCTYPE 根元素 PUBLIC “DTD 名称”“DTD 文档的 URL” >

2 XML解析器

解析 XML 技术(dom 和 sax),需要一个解析器。

  • JAXP(Java AOI for XML Processing):是 SUN 公司推出的解析标准实现。
  • Dom4J:是开源组织推出的解析开发包。(实际开发中常用)
  • JDom:是开源组织推出的解析开发包。

2.1 JAXP-DOM解析

解析的逻辑同Python中差不多,如果了解其中一种语言,其他语言基本上可以去看下。

在JDK中,可以在rt.jar包中找到解析方法。

【Java】基础知识-JAXP解析XML(一)

「步骤」

1、创建 DOM 解析器的工厂,得到 DOM 解析器对象

2、解析 XML 文档,得到代表整个文档的 Document 对象,将其放在内存中

3、获取根元素集合

4、解析处理

2.2 创建实验xml

首先创建一个xml,这里创建一个persons.xml,文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
<persons>
    <person sid="001">
        <name>张小帅</name>
        <sex></sex>
        <age>18</age>
    </person>
    <person sid="002">
        <name>刘晓萌</name>
        <sex></sex>
        <age>21</age>
    </person>
    <person sid="003">
        <name>王老四</name>
        <sex></sex>
        <age>38</age>
    </person>
</persons>

2.3 解析技术DOM

创建DomParserXmlTest.java,内容如下。

package com.liuyc.tooljdk.xml;

import org.w3c.dom.*;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * <p> DomParserXmlTest </p>
 */
public class DomParserXmlTest {

    public static void main(String[] args) {
        String xmlPath = "./tool-jdk8/src/main/java/com/liuyc/tooljdk/xml/persons.xml";
        try {
            // 1、创建 DOM 解析器的工厂,得到 DOM 解析器对象
            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();

            // 2、解析 XML 文档,得到代表整个文档的 Document 对象,将其放在内存中
            Document document = builder.parse(xmlPath);

            // 3、获取根元素 persons
            System.out.println("----------------------------------------------------------------------");
            Element root = document.getDocumentElement();
            System.out.println("根元素节点名称:" + root.getNodeName());
            System.out.println("根元素节点类型(是一个元素:Element = 1):" + root.getNodeType());
            System.out.println("----------------------------------------------------------------------");

            NodeList personNode = document.getElementsByTagName("person");
            System.out.println("person节点数量:" + personNode.getLength());
            Node personEle = personNode.item(1);
            System.out.println("person元素节点名称:" + personEle.getNodeName());
            System.out.println("person元素节点文本内容:" + personEle.getTextContent());
            System.out.println("person元素节点属性名称:" + personEle.getAttributes().item(0).getNodeName());
            System.out.println("person元素节点属性值:" + personEle.getAttributes().item(0).getNodeValue());
            System.out.println("person元素节点属性类型(是一个属性:Attr = 2):" + personEle.getAttributes().item(0).getNodeType());
            System.out.println("----------------------------------------------------------------------");

            NodeList names = document.getElementsByTagName("name");
            System.out.println("所有name元素标签内存地址:" + names);
            System.out.println("索引为1的name元素标签名称:" + names.item(1).getNodeName());
            System.out.println("索引为1的name元素标签的值:" + names.item(1).getTextContent());
            System.out.println("----------------------------------------------------------------------");

            // 4、解析
            NodeList list = root.getChildNodes();
            ArrayList<Map<String, Object>> arr = new ArrayList<>();
            for (int i = 0; i < list.getLength(); i++) {

                // 遍历所有person节点
                Node item = list.item(i);
                if (item.getNodeType() == Node.ELEMENT_NODE) {
                    Map<String, Object> map = new HashMap<>();
                    NamedNodeMap attributes = item.getAttributes();

                    // 遍历所有person属性
                    for (int j = 0; j < attributes.getLength(); j++) {
                        Node nodePerson = attributes.item(j);
                        map.put(nodePerson.getNodeName(), nodePerson.getNodeValue());
                    }

                    // 遍历所有person节点的内容
                    NodeList list2 = item.getChildNodes();
                    for (int j = 0; j < list2.getLength(); j++) {
                        Node item2 = list2.item(j);
                        if (item2.getNodeType() == Node.ELEMENT_NODE) {
                            Node node = item2.getFirstChild();
                            if (item2.getNodeName().equals("name")) {
                                map.put("name", node.getTextContent());
                            }
                            if (item2.getNodeName().equals("sex")) {
                                map.put("sex", node.getTextContent());
                            }
                            if (item2.getNodeName().equals("age")) {
                                map.put("age", Integer.parseInt(node.getTextContent()));
                            }
                        }
                    }
                    arr.add(map);
                }
            }
            arr.forEach(System.out::println);
        } catch (ParserConfigurationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SAXException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

2.4 执行结果

执行上面代码获取的结果如下:


根元素节点名称:persons

根元素节点类型(是一个元素:Element = 1):1


person节点数量:3

person元素节点名称:person

person元素节点文本内容:

 刘晓萌

 女

 21

person元素节点属性名称:sid

person元素节点属性值:002

person元素节点属性类型(是一个属性:Attr = 2):2


所有name元素标签内存地址:com.sun.org.apache.xerces.internal.dom.DeepNodeListImpl@27d6c5e0

索引为1的name元素标签名称:name

索引为1的name元素标签的值:刘晓萌


{sex=男, name=张小帅, age=18, sid=001}

{sex=女, name=刘晓萌, age=21, sid=002}

{sex=男, name=王老四, age=38, sid=003}

还有一些其他方法,例如:

获取第一个节点:getFirstChild()

获取最后一个节点:getLastChild()

其他的一些属性,建议参考下JDK的源代码来加深理解。

2.5 新增节点

「步骤」

1、创建position_level元素

2、创建position_level的文本

3、把文本添加到position_level

4、把 position_level 添加到 索引为1的 person 下面

5、回写 xml

// --------------------------------------------------------------------------
            // 5、为person新增一个标签:职级等级(position_level),他的内容是:三级

            // 创建position_level元素
            Element pLevel = document.createElement( "position_level" );

            // 创建position_level的文本
            Text pLevelText = document.createTextNode( "三级" );

            // 把文本添加到position_level
            pLevel.appendChild(pLevelText);

            // 把 position_level 添加到 索引为1的 person 下面
            personEle.appendChild(pLevel);

            // 回写 xml
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            transformer.transform(new DOMSource(document), new StreamResult(xmlPath));
            // --------------------------------------------------------------------------

执行完成代码之后,我们查看下原来的persons.xml,可以看到我们增加的标签position_level已经增加进来。

【Java】基础知识-JAXP解析XML(一)

也可以看出来,我们新增的节点并没有美化展示到xml中,而且还给我们增加了一个属性standalone="no"。

2.6 修改节点

「步骤」

1、得到age元素

2、修改age值,设置到age元素上

3、回写xml,使之生效

            // --------------------------------------------------------------------------
            // 6、修改第一个人的年龄为28
            System.out.println("----------------------------------------------------------------------");

            // 得到age
            Node ageNode = document.getElementsByTagName("age").item(0);
            System.out.println("原始年龄:" + ageNode.getTextContent());

            // 修改age值
            ageNode.setTextContent("28");
            System.out.println("修改后的年龄:" + ageNode.getTextContent());

            // 回写xml
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            transformer.transform(new DOMSource(document), new StreamResult(xmlPath));
            // --------------------------------------------------------------------------

执行完成代码之后,我们查看下原来的persons.xml

原始年龄:18
修改后的年龄:28

【Java】基础知识-JAXP解析XML(一)

可以看到我们增加的标签age已经增加进来。

2.7 删除节点

删除刚才的新增的position_level节点。

「步骤」

1、获取position_level节点元素

2、得到position_level父节点

3、使用父节点删除当前节点操作

4、回写xml,使之生效

          // 7、删除position_level节点
            System.out.println("----------------------------------------------------------------------");

            // 获取position_level节点元素
            Node pLevel = document.getElementsByTagName("position_level").item(0);

            // 得到position_level父节点
            Node pLevelParentNode = pLevel.getParentNode();

            // 使用父节点删除当前节点操作
            pLevelParentNode.removeChild(pLevel);

            // 回写xml
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            transformer.transform(new DOMSource(document), new StreamResult(xmlPath));

执行完成代码之后,我们查看下原来的persons.xml已经没有position_level这个节点元素了。

【Java】基础知识-JAXP解析XML(一)

3 DOM-API

在DOM中,主要适用的是元素和节点以及属性。

针对元素有如下方法:

【Java】基础知识-JAXP解析XML(一)

针对节点有如下方法:

【Java】基础知识-JAXP解析XML(一)

DocumentBuilder API

【Java】基础知识-JAXP解析XML(一)

DocumentBuilderFactory API

【Java】基础知识-JAXP解析XML(一)

w3c标准

【Java】基础知识-JAXP解析XML(一)