一、概述
- 可扩展标记语言(Extensible Markup Language)
- .XML文件是保存XML数据的一种方式,XML数据也可以以其他的方式存在(如在内存中构建XML数据),不要将XML语言狭隘地理解为XML文件
- XML通常指的是具有特定格式的字符串
- 特点
- XML具有平台无关性,是一门独立的标记语言
- 不同操作系统都可以打开XML文件(与操作系统的无关性)
- 不同的编程语言解析同一份XML文件的结果都是一样的(与编程语言的无关性)
- XML文件独立存在也是有意义的
- XML具有自我描述性
- 文件首行就是XML文档声明
- XML具有平台无关性,是一门独立的标记语言
- 作用
- 可用于网络数据传输
- 可用于数据存储
- 可用于配置文件的编写
二、XML的语法格式
1. XML文档声明
<?xml version="1.0" encoding="UTF-8"?>
2、标记(元素/标签/节点)
XML文档,由一个个的标记组成.
语法:
开始标记(开放标记): <标记名称>
结束标记(闭合标记): </标记名称>
标记名称: 自定义名称,必须遵循以下命名规则:
1.名称可以含字母、数字以及其他的字符
2.名称不能以数字或者标点符号开始
3.名称不能以字符 “xml”(或者 XML、Xml)开始
4.名称不能包含空格,不能包含冒号(:)
5.名称区分大小写
标记内容: 开始标记与结束标记之间 ,是标记的内容.
例如 ,我们通过标记, 描述一个人名:
<name>张三</name>
3、一个XML文档中, 必须有且且仅允许有一个根标记
正例:
<names>
<name>张三</name>
<name>李四</name>
</names>
反例:
<name>李四</name>
<name>麻子</name>
4、标记可以嵌套,但是不允许交叉
正例:
<person>
<name>李四</name>
<age>18</age>
</person>
反例:
<person>
<name>李四<age></name>
18</age>
</person>
5、标记的层级称呼 (子标记, 父标记 , 兄弟标记, 后代标记 ,祖先标记)
例如:
<persons>
<person>
<name>李四</name>
<length>180cm</length>
</person>
<person>
<name>李四</name>
<length>200cm</length>
</person>
</persons>
name是person的子标记.也是person的后代标记
name是persons的后代标记
name是length的兄弟标记
person是name的父标记
persons是name的祖先标记
6、标记名称允许重复
7、标记除了开始和结束 , 还有属性
- 标记中的属性, 在标记开始时 描述, 由属性名和属性值 组成
- 格式
- 在开始标记中, 描述属性可以包含0-n个属性, 每一个属性是一个键值对!
属性名不允许重复 , 键与值之间使用等号连接, 多个属性之间使用空格分割.
属性值 必须被引号引住
- 在开始标记中, 描述属性可以包含0-n个属性, 每一个属性是一个键值对!
- 举例
<persons>
<person id="10001" groupid="1">
<name>李四</name>
<age>18</age>
</person>
<person id="10002" groupid="1">
<name>李四</name>
<age>20</age>
</person>
</persons>
8、注释
-
注释不能写在文档的文档声明前
-
注释不能嵌套注释
-
格式
-
注释开始:<!--
-
注释结束:-->
-
9、语法进阶之CDATA(了解)
- CDATA 是不应该由 XML 解析器解析的文本数据
- 像 "<" 和 "&" 字符在 XML 元素中都是非法的
- "<" 会产生错误,因为解析器会把该字符解释为新元素的开始
- "&" 会产生错误,因为解析器会把该字符解释为字符实体的开始
- 某些文本,比如 JavaScript 代码,包含大量 "<" 或 "&" 字符。为了避免错误,可以将脚本代码定义为 CDATA
- CDATA 部分中的所有内容都会被解析器忽略
- CDATA 部分由 "<![CDATA[" 开始,由 "]]>" 结束
- 像 "<" 和 "&" 字符在 XML 元素中都是非法的
三、java解析XML的方式
1、面试题
问题:java中有几种XML解析方式?分别是什么?有什么样的优缺点?
回答:
- 四种,但本质是两种,即SAX解析和DOM解析
1、SAX解析
- 解析的方式是事件驱动机制
- SAX解析器会逐行读取XML文件解析,每当解析到一个标签的开始/结束/内容/属性时,触发事件
- 我们可以编写程序在这些事件发生时,进行相应的处理
- 优点
- 分析能够立即开始,而不是等待所有的数据加载完毕后再处理
- 逐行加载,也就是内存中永远只会有一行的数据,能够节省内存,有助于解析大于系统内存的文档
- 有时候不必解析整个文档,它可以在某个条件得到满足时停止解析
- 缺点
- 单向解析,无法定位文档层次,无法同时访问同一文档的不同部分数据
- 因为SAX是逐行解析,解析到n行时,n-1行已经被释放了,无法再对其进行操作
- 无法得知事件发生时元素的层次,只能自己维护节点的父子关系
- 只读解析方式,无法修改XML文档的内容
- 单向解析,无法定位文档层次,无法同时访问同一文档的不同部分数据
2、DOM解析
- 是用与平台和语言无关的方式表示XML文档的官方w3c标准,分析该结构通常需要加载整个文档,并在内存中建立文档树模型。程序员可以通过操作文档树,来完成数据的获取、修改、删除等
- 优点
- 文档在内存中加载,允许对数据和结构做出更改
- 访问是双向的,可以在任何时候在树中双向解析数据
- 缺点
- 文档全部加载在内存中,消耗资源大(可忽略,因为XML文件实际上并不会很大,所以DOM相当于没有缺点,经常使用该方法解析XML文件)
3、JDOM解析
- 目的是成为java特定文档模型,它简化与XML的交互,并且比使用DOM实现更快。由于是第一个java特定模型,JDOM一直得到大力推广和促进
- JDOM文档声明其目的是“使用20%或更少的经历解决80%或更多java/XML问题”
- 优点
- 使用具体类而不是接口,简化了DOM的API
- 大量使用了java集合类,方便java开发人员操作
- 缺点
- 没有较好的灵活性
- 性能不是那么优异
4、DOM4J解析
- 它是JDOM的一种智能分支,合并了许多超出基本XML文档表示的功能,包括集成的XPath支持、XML、Schema支持以及用于大文档或流化文档的基于事件的处理。
- 它还提供了构建文档表示的选项,是一个非常优秀的Java XML API,具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件,如今可以看到越来越多的java软件都在使用DOM4J来读写XML
- 目前许多开源项目中大量采用DOM4J,例如Hibernate
四、java生成XML
- 导包
- 这三个包下面解析XML的例子也会用到
- 步骤和方法解析
1、通过文档帮助器 (DocumentHelper) , 创建空的文档对象
Document doc = DocumentHelper.createDocument();
2、通过文档对象, 向其中添加根节点
Element root = doc.addElement("根节点名称");
3、通过根节点对象root , 丰富我们的子节点
Element e = root.addElement("元素名称");
4、创建一个文件输出流 ,用于存储XML文件
FileOutputStream fos = new FileOutputStream("要存储的位置");
5、将文件输出流, 转换为XML文档输出流
XMLWriter xw = new XMLWriter(fos);
6、写出文档
xw.write(doc);
7、释放资源
xw.close();
- 举例
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.XMLWriter;
import java.io.FileOutputStream;
import java.io.IOException;
public class demo1 {
public static void main(String[] args) throws IOException {
//通过文档帮助器创建一个空的文档对象
Document document = DocumentHelper.createDocument();
//创建一个根结点
Element books = document.addElement("books");
//在根结点下继续创建节点
for (int i=0;i<3;i++){
//创建book节点
Element book = books.addElement("book" + i);
//向book节点添加id属性
book.addAttribute("id",i+"");
//向book节点添加两个子节点
Element name = book.addElement("name");
Element info = book.addElement("info");
//设置子节点的内容
name.setText("金苹果"+i);
info.setText("金苹果的故事"+i);
}
//创建文件的输出流
FileOutputStream fos = new FileOutputStream("books.xml");
//将文件输出流转换为XML文档输出流
XMLWriter xmlWriter = new XMLWriter(fos);
//写出xml文档
xmlWriter.write(books);
//关闭流
fos.close();
}
}
- 生成的xml文档展示
五、DOM4J解析XML文档
1、步骤
1、引入dom4j.jar文件
2、创建一个指向XML文件的输入流
FileInputStream fis = new FileInputStream("xml文件的地址");
3、创建一个XML读取工具对象
SAXReader sr = new SAXReader();
4、使用读取工具对象, 读取XML文档的输入流 , 并得到文档对象
Document doc = sr.read(fis);
5、通过文档对象, 获取XML文档中的根元素对象
Element root = doc.getRootElement();
2、文档对象Document的常用API
文档对象指的是加载到内存的整个XML文档
- 通过文档对象,获取XML文档中的根元素对象
Element root = doc.getRootElement();
- 添加根结点
Element root = doc.addElement("根节点名称");
3、元素对象Element常用API
元素对象指的是XML文档中的单个节点
- 获取节点名称
String getName();
- 获取节点内容
String getText();
- 设置节点内容
String setText();
- 根据子节点的名称,获取匹配名称的第一个子节点对象
Element element(String 子节点名称);
- 获取所有的子节点对象
List<Element> elements();
- 获取节点的属性值
String attributeValue(String 属性名称);
- 获取子节点的内容
String elementText(String 子节点名称);
- 添加子节点
Element addElement(String 子节点名称);
- 添加属性
void addAttribute(String 属性名,String 属性值);
4、举例:解析本地文件
- 以读取刚刚创建的books.xml为例
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;
public class demo2 {
public static void main(String[] args) throws IOException, DocumentException {
//1、创建一个XML文件的输入流
FileInputStream fis = new FileInputStream("books.xml");
//2、创建一个XML文件的读取工具对象
SAXReader reader = new SAXReader();
//3、读取XML文件并获取文档对象
Document document = reader.read(fis);
//4、通过文档对象获取根结点
Element rootElement = document.getRootElement();
//5、通过根节点获取所有子节点
List<Element> elements = rootElement.elements();
//6、循环遍历节点数据
for (Element e : elements){
//获取属性id值
String id = e.attributeValue("id");
//获取子节点name的内容
String name = e.element("name").getText();
//获取子节点info的内容
String info = e.element("info").getText();
System.out.println("id="+id+",name="+name+",info="+info);
}
//7、关闭流
fis.close();
}
}
- 运行结果
5、举例:解析网络文件案例
- 网址
http://apis.juhe.cn/mobile/get?phone=18375072028&dtype=xml&key=9f3923e8f87f1ea50ed4ec8c39cc9253
- 网址的效果
- 解析这个网址XML内容的代码
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class demo3 {
public static void main(String[] args) throws IOException, DocumentException {
String phone = "18375072028";
//1. 获取到XML资源的输入流
URL url = new URL("http://apis.juhe.cn/mobile/get?phone=" + phone + "&dtype=xml&key=9f3923e8f87f1ea50ed4ec8c39cc9253");
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();
//2. 创建一个XML读取对象
SAXReader sr = new SAXReader();
//3. 通过读取对象 读取XML数据,并返回文档对象
Document doc = sr.read(is);
//4. 获取根节点
Element root = doc.getRootElement();
//5. 解析内容
String code = root.elementText("resultcode");
if ("200".equals(code)) {
Element result = root.element("result");
String province = result.elementText("province");
String city = result.elementText("city");
if (province.equals(city)) {
System.out.println("手机号码归属地为:" + city);
} else {
System.out.println("手机号码归属地为:" + province + " " + city);
}
} else {
System.out.println("请输入正确的手机号码");
}
}
}
- 运行效果
六、DOM4J-XPATH解析XML
1、路径表达式
- 通过路径快速查找一个或一组元素
- 路径表达式
/ :从根节点开始查找
// :从发起查找的结点位置,查找后代节点(常用)
. : 查找当前节点
.. : 查找父节点
@ : 选择属性
属性使用方式:
[@属性名='值']
[@属性名>'值']
[@属性名<'值']
[@属性名!='值']
- 举例
- 查找book属性的id为1的节点,获取该节点的name值
- 路径://book[@id='1']//name
2、使用步骤
- 通过Node类的两个方法来完成查找
- Node是Document与Element的父接口
- 方法1
- 根据路径表达式,查找匹配的单个节点
Element e = selectSingleNode("路径表达式");
- 方法2
- 根据路径表达式,查找匹配的所有节点
List<Element> es = selectNodes("路径表达式");
3、举例
- 还是以上一节解析网络上的XML文件为例
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class demo4 {
public static void main(String[] args) throws IOException, DocumentException {
String phone = "18313935565";
//1. 获取到XML资源的输入流
URL url = new URL("http://apis.juhe.cn/mobile/get?phone="+phone+"&dtype=xml&key=9f3923e8f87f1ea50ed4ec8c39cc9253");
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();
//2. 创建一个XML读取对象
SAXReader sr = new SAXReader();
//3. 通过读取对象 读取XML数据,并返回文档对象
Document doc = sr.read(is);
Node node = doc.selectSingleNode("//company");
System.out.println("运营商:"+node.getText());
is.close();
}
}
- 运行效果