【Java】基础知识-JAXP解析XML(一)
1 XML概述
1.1 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 特殊字符
「特殊转义符」
转义后 | 转义前 | 解释描述 |
---|---|---|
< | < | less than |
> | > | greater than |
& | & | ampersand |
' | ' | apostrophe |
" | " | 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包中找到解析方法。
「步骤」
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已经增加进来。
也可以看出来,我们新增的节点并没有美化展示到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
可以看到我们增加的标签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这个节点元素了。
3 DOM-API
在DOM中,主要适用的是元素和节点以及属性。
针对元素有如下方法:
针对节点有如下方法:
DocumentBuilder API
DocumentBuilderFactory API
w3c标准