XML基础/XML解析
为什么要学XML
XML是一种通用的数据交换格式
许多系统的配置文件都使用XML格式
xml可以跨平台,主流各种平台都对xml有支持, 真正的跨平台.
xml 是各种应用程序之间进行数据传输的最常用的工具。
掌握XML是软件开发人员的一项基本技能
任意一个javaee框架中都要用到XML。
XML结构清晰(树状结构),不仅让人能够明白,还让计算机也能够明白。
XML作为一种公订的、开放的标准,不受知识产权的限制.(免费)
具有结构性的数据应该使用xml来存取,这样看上去结构更加的清晰(如省市区的数据)
XML是什么
XML(eXtensible Markup Language),是一种可扩展的标记语言,类似HTML。如: <h1></h1>
XML技术是W3C组织(World Wide Web Consortium万维网联盟)发布的,目前遵循的是W3C组织于1998年发布的XML1.0规范。
XML被广泛认为是继Java之后在Internet上最激动人心的新技术。
HTML: 显示页面,网页. 学习里面自带的标签<html>
XML: 主要是用于描述配置信息。
XML标签没有被预定义,需要用户自行定义标签。
XML的语法
- 文档声明:
在编写XML文档时,需要先使用文档声明来声明XML文档。且必须出现在文档的第一行。
最简单的语法:
如:<?xml version="1.0"?>
用encoding属性说明文档所使用的字符编码,默认为UTF-8。
如:<?xml version="1.0" encoding="UTF-8"?>
用standalone属性说明文档是否独立,即是否依赖其他文档。yes表示独立文档不依赖,no表示要依赖,可以不写则表示可以依赖可以不依赖
如:<?xml version="1.0" standalone="no"?>
- 编码:XML有两个编码: 保存在磁盘上的文件编码要与声明的编码一致。要保证两个编码相同,都为UTF-8
内容编码:文档声明中指定
文件本身的编码:文件另存为时可以选择
- 元素(标签)<>(可以自定义标签)
XML文档必须有一个根元素.
标签区分大小写的.<a> <A>
有开始标签就必须有结束标签.<linkman></linkman> 或者 自闭合<linkman/>
不允许标签交叉嵌套
- 属性:
属性是元素的一部分,它必须出现在元素的开始标签中
key=value格式,尽量在编写的时候 value 前后不要有空格
属性的key不能使用空格,冒号特殊字符.
属性的key需要以字母开头.
属性的value必须使用引号或者双引号
-
5.注释
如:<!-- 注释 -->
6.命名规则
在XML中,可以使用自己需要的元素来扩展标记语言。
命名基本规则:
·元素名称不能以数字或特殊字符开头
·可以包含字母,数字,下划线等
·不能以字符串“XML”作为开头
·不能包含空格
·尽量不要包含特殊字符
·区分大小写
XML文件的约束(了解)
为什么要有XML约束
XML本身是可扩展的,用户可以写任意的标签,但是这样一来就可能乱套了,用户的随意会让XML表示的含义不再统一并且解析会变得困难,所以一个框架不应该让用户任意定义标签。我们需要有约束来规范XML可以出现哪些标签,不能出现哪些标签以及标签之间的顺序、出现的次数,标签有哪些属性等。
我们编写XML文件的时候有约束的话还可以根据约束给我们提示该怎么写
XML文件约束的分类
xml 约束分两种: dtd 约束,schama 约束
什么是DTD
DTD(Document Type Definition),文档类型定义,用来约束XML文档。规定XML文档中元素的名称,子元素的名称及顺序,元素的属性等。
一个xml文档中只能添加一个DTD约束
DTD约束的语法
元素声明
定义元素语法:<!ELEMENT 元素名 元素描述>
元素名:自定义
元素描述包括:符号和数据类型
常见符号:? * + () | ,
常见类型:#PCDATA 表示内容是文档,不能是子标签
DTD用法
学习了dtd 约束 ,为何要还学习schema约束呢?
因为dtd约束有局限性 :一个xml文档只能有一个dtd约束
什么是Schema
Schema是新的XML文档约束;
Schema要比DTD强大很多,是DTD 替代者;
Schema本身也是XML文档,但Schema文档的扩展名为xsd,而不是xml。
Schema 功能更强大,数据类型更完善
一个xml文档中可以添加多个schema约束
为何支持多个schema 约束 就是因为 Schema 支持名称空间
什么是命名空间
如果一个XML文档中使用多个Schema文件,而这些Schema文件中定义了相同名称的元素时就会出现名字冲突。这就像一个Java文件中使用了import java.util.和import java.sql.时,在使用Date类时,那么就不明确Date是哪个包下的Date了。
总之名称空间就是用来处理元素和属性的名称冲突问题,与Java中的包是同一用途。如果每个元素和属性都有自己的名称空间,那么就不会出现名字冲突问题,就像是每个类都有自己所在的包一样,那么类名就不会出现冲突。
Schema语法
Schema用法
XML解析
XML文档结构分析
开发中比较常见的解析方式
有两种,如下:
-
A. DOM:要求解析器把整个XML文档装载到内存,并解析成一个Document对象。
优点:元素与元素之间保留结构关系,故可以进行增删改查操作。
缺点:XML文档过大,可能出现内存溢出显现。
-
B. SAX:是一种速度更快,更有效的方法。它逐行扫描文档,一边扫描一边解析。并以事件驱动的方式进行具体解析,每执行一行,都将触发对应的事件。(了解)
优点:处理速度快,可以处理大文件
缺点:只能读,逐行后将释放资源。
获取Document对象
要操作XML,先就得有Document对象:
把一个XML文件加载进内存的时候,在内存中形成所谓的一种树状结构,我们把这一个结构称之dom树,创建的对象叫Document对象.
注意:
我们在Java代码中所做的增/删/改/查操作,都仅仅是操作的是内存中的Document对象,和磁盘中的XML文件没有关系.如图:
比如:删除一个联系人信息之后,XML文件中数据依然存在,此时出现内存中的数据和磁盘文件中的数据不同步,所以,对于增删改操作,我们需要做同步操作(把内存中的数据和磁盘的XML文件数据保持一致).
DOM:在第一次的时候就会把XML文件加载进内存,如果XML文件过大,可能会造成内存的溢出.
DOM:在做增删改查操作的时候比较简单,但是性能却不高(线性搜索).
// 首先要获取Document对象,如何获取呢?
前提是必须有 xml文件
File file = new File("C:/stsworkspace/day03/contacts.xml");
注意:
当导入类/接口的时候,导入的包是org.w3c.dom ,因为xml遵循的是w3c组织的规范
思路:
1.获取一个DocumentBuilderFactory工厂对象
通过DocumentBuilderFactory调用newInstance 方法来获取工厂对象 DocumentBuilderFactory.newInstance();
2.获取DocumentBuilder对象
通过工厂对象调用newDocumentBuilder方法来获取 :工厂对象.newDocumentBuilder()
3.通过documentBuilder对象,获取document对象
获取的方式有两种:当xml文件不存在的时候,可以调用newDocument方法来创建一个Document对象
当xml文件存在的情况,可以调用parse方法来从xml文件中解析得到一个Document对象
如图:
DOM中常用的API
Node接口:
String getTextContent():获取元素的文本内容
void setTextContent(String textContent):给元素设置新的文本内容
Node appendChild(Node newChild):往当前元素中增加新的儿子.
老爸.appendChild(新的儿子);
Node removeChild(Node oldChild):从当前节点下删除指定的儿子节点.
Document接口:
Element getDocumentElement():获取并返回文档的根元素对象.
Element getElementById(String elementId):根据指定的id属性的值,获取对应的Element对象.
若当前解析的XML文件没有约束文件,并且没有对id属性有ID类型约束,则不能使用该方法.
NodeList getElementsByTagName(String tagName):根据指定的标签名获取文档中所有的子元素.
Element createElement(String tagName):根据指定的元素名,创建Element对象.
Element接口:
NodeList getElementsByTagName(String tagName):根据指定的标签名获取当前元素下(this)所有的子元素.
void setAttribute(String attrName, String attrValue):为当前元素对象设置指定名子的属性和属性值.
String getAttribute(String attrName):获取当前元素指定属性名的属性值.
DOM操作练习
需求:得到某个具体的文本节点的内容:取出第二个联系人的名字
// 需求:获取第二个联系人的姓名
@Test
public void testQuery() throws Exception {
// 1. 获取document对象
Document document = DocumentBuilderFactory.newInstance()//获取工厂对象
.newDocumentBuilder() //获取构建器
.parse(file);
// 2. 获取文档中的根元素contacts
Element root = document.getDocumentElement();
// 3. 获取contacts根元素下面的第二个子元素 linkman
Element linkmanEle = (Element) root.getElementsByTagName("linkman").item(1);
// 4. 获取linkman元素下面的子元素name
Node nameEle = linkmanEle.getElementsByTagName("name").item(0);
// 5. 获取name元素对应的文本内容
String content = nameEle.getTextContent();
System.out.println(content);
}
需求:修改某个元素节点的主体内容:第一个联系人的姓名为“马云”
// 需求:修改第一个联系人的姓名为“马云”
@Test
public void testUpdate() throws Exception {
// 1. 获取document对象
Document document = DocumentBuilderFactory.newInstance()//获取工厂对象
.newDocumentBuilder() //获取构建器
.parse(file);
// 2. 获取文档中的根元素contacts
Element root = document.getDocumentElement();
// 3. 获取contacts根元素下面的第二个子元素 linkman
Element linkmanEle = (Element) root.getElementsByTagName("linkman").item(0);
// 4. 获取linkman元素下面的子元素name
Node nameEle = linkmanEle.getElementsByTagName("name").item(0);
//5. 修改name元素对应的文本内容
nameEle.setTextContent("马云");
//6. 同步数据:把内存中的数据同步到磁盘上 使用的核心类: Transformer
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(new DOMSource(document), new StreamResult(file));
}
如图:
需求:向指定元素节点中增加子元素节点:增加一个联系人信息
// 需求:往xml中新增一个新的联系人
@Test
public void testAdd() throws Exception {
// 1. 获取document对象
Document document = DocumentBuilderFactory.newInstance()//获取工厂对象
.newDocumentBuilder() //获取构建器
.parse(file);
// 2. 获取文档中的根元素contacts
Element root = document.getDocumentElement();
// 3. 创建元素(linkman,name,address,group)
Element linkmanEle = document.createElement("linkman");
Element nameEle = document.createElement("name");
Element addressEle = document.createElement("address");
Element groupEle = document.createElement("group");
// 4. 给上面创建的元素设置文本内容
linkmanEle.setAttribute("id", "3");
nameEle.setTextContent("李清照");
addressEle.setTextContent("上海青浦区");
groupEle.setTextContent("叩丁狼教育");
// 5. 把元素name,address ,group 设置到linkman中
linkmanEle.appendChild(nameEle);
linkmanEle.appendChild(addressEle);
linkmanEle.appendChild(groupEle);
// 6. 把linkman元素设置到contacts中
root.appendChild(linkmanEle);
// 7. 同步数据:把内存中的数据同步到磁盘上
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(new DOMSource(document), new StreamResult(file));
}
需求:操作XML文件属性:设置/获取联系人的id属性
// 需求:设置/获取第一个联系人的id属性值为10
@Test
public void testDelete() throws Exception {
// 1. 获取document对象
Document document = DocumentBuilderFactory.newInstance()//获取工厂对象
.newDocumentBuilder() //获取构建器
.parse(file);
// 2. 获取文档中的根元素contacts
Element root = document.getDocumentElement();
// 3. 获取contacts根元素下面的第一个子元素 linkman
Element linkmanEle = (Element) root.getElementsByTagName("linkman").item(0);
// 4. 设置属性的值为10
linkmanEle.setAttribute("id",10);
// 5. 获取属性的值
System.out.println("id的值为:" +linkmanEle.getAttribute("id") );
//6. 同步数据:把内存中的数据同步到磁盘上 使用的核心类: Transformer
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(new DOMSource(document), new StreamResult(file));
}
}
需求:删除指定元素节点:删除第三个联系人信息
// 需求:删除第三个联系人信息
@Test
public void testDelete() throws Exception {
// 1. 获取document对象
Document document = DocumentBuilderFactory.newInstance()//获取工厂对象
.newDocumentBuilder() //获取构建器
.parse(file);
// 2. 获取文档中的根元素contacts
Element root = document.getDocumentElement();
// 3. 获取contacts根元素下面的第三个子元素 linkman
Element linkmanEle = (Element) root.getElementsByTagName("linkman").item(2);
// 4. 删除第三个联系人信息
root.removeChild(linkmanEle);
//方式二:linkmanEle.getParentNode().removeChild(linkmanEle);
//6. 同步数据:把内存中的数据同步到磁盘上 使用的核心类: Transformer
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(new DOMSource(document), new StreamResult(file));
}
}