day17_xml
一、课程目标
1. 【理解】什么是XML
2. 【掌握】XML的书写与组成
3. 【理解】XML约束
4. 【掌握】XML四种解析方式
二、XML概述
2.1 XML概念
Extensible Markup Language 可扩展标记语言
可扩展:标签都是自定义的。
2.2 XML作用
常用于存储数据和配置文件
最常用的功能就是xml做为一个配置文件
数据的传输–> 被json替代
2.3 XML基础语法
- xml文档的后缀名
.xml
- xml第一行必须定义为
文档声明
- xml文档中有且仅有一个
根标签
- 属性值必须使用
引号(单双都可)
引起来- 标签必须正确关闭
- xml标签名称
严格区分大小写
2.4 XML快速入门
<?xml version='1.0' ?>
<users>
<user id='1'>
<name>zhangsan</name>
<age>23</age>
<gender>male</gender>
<br/>
</user>
<user id='2'>
<name>lisi</name>
<age>24</age>
<gender>female</gender>
</user>
</users>
三、XML组成部分
3.1 文档声明
格式
<?xml 属性列表 ?>
属性列表
version
:版本号,必须的属性【1.0】encoding
:编码方式。告知解析引擎当前文档使用的字符集,默认值:ISO-8859-1standalone
:是否独立
- 取值:
- yes:不依赖其他文件
- no:依赖其他文件
3.2 指令(了解)
<?xml-stylesheet type="text/css" href="a.css" ?>
3.3 标签
标签命名规则
- 名称可以包含字母、数字以及其他的字符
- 名称不能以数字或者标点符号开始
- 名称不能以字母 xml(或者 XML、Xml 等等)开始
- 名称不能包含空格
最佳命名习惯
- 名称应当比较简短,比如:<book_title>,而不是:<the_title_of_the_book>。
- 避免 “-” 字符。如果您按照这样的方式进行命名:“first-name”,一些软件会认为你需要提取第一个单词。
- 避免 “.” 字符。如果您按照这样的方式进行命名:“first.name”,一些软件会认为 “name” 是对象 “first” 的属性。
- 避免 “:” 字符。冒号会被转换为命名空间来使用(稍后介绍)。
- XML 文档经常有一个对应的数据库,其中的字段会对应 XML 文档中的元素。有一个实用的经验,即使用数据库的名称规则来命名 XML 文档中的元素。
- 非英语的字母比如 éòá 也是合法的 XML 元素名,不过需要留意当软件开发商不支持这些字符时可能出现的问题。
3.4 属性
属性值必须被引号包围,不过单引号和双引号均可使用。比如一个人的性别,person 标签可以这样写:
<person sex="female">
或
<person sex='female'>
id属性
有时候会向元素分配 ID 引用。这些 ID 索引可用于标识 XML 元素.
<messages>
<note id="501">
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
<note id="502">
<to>John</to>
<from>George</from>
<heading>Re: Reminder</heading>
<body>I will not</body>
</note>
</messages>
注意事项
id属性值唯一
3.5 文本
CDATA区:在该区域中的数据会被原样展示
语法
<![CDATA[ 数据 ]]>
四、XML约束
一个良好的 XML 文档要满足以下规则:
- XML 文档必须有根元素
- XML 文档必须有关闭标签
- XML 标签对大小写敏感
- XML 元素必须被正确的嵌套
- XML 属性必须加引号
4.1 什么是约束
规定xml文档的书写规则
4.2 约束的使用
定义xml标签约束,使开发者按照定义书写,也可以约束html文件
4.3 约束的分类
4.3.1 DTD约束
document type definition 文档类型定义
4.3.1.1 DTD约束编写
扩展名是:dtd
<!ELEMENT students (student*) >
<!ELEMENT student (name,age,sex)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
<!ELEMENT sex (#PCDATA)>
<!ATTLIST student number ID #REQUIRED>
4.3.1.2 DTD约束引入
格式
* 内部dtd:将约束规则定义在xml文档中
* 外部dtd:将约束的规则定义在外部的dtd文件中
* 本地:<!DOCTYPE 根标签名 SYSTEM "dtd文件的位置">
* 网络:<!DOCTYPE 根标签名 PUBLIC "dtd文件名字" "dtd文件的位置URL">
案例演示
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE students SYSTEM "student.dtd">
<students>
<student number="D001">
<name>tom</name>
<age>18</age>
<sex>male</sex>
</student>
</students>
4.3.2 Schema约束
比dtd 更能更加强大。 提供了更加丰富的数据类型
XML Schema 可针对未来的需求进行扩展
XML Schema 更完善,功能更强大
XML Schema 基于 XML 编写 (schema本质上就是一个 xml文件)
XML Schema 支持数据类型 (提供的更加丰富的数据类型)
XML Schema 支持命名空间
4.3.2.1 Schema约束编写
扩展名是:xsd
<?xml version="1.0"?>
<xsd:schema xmlns="http://www.itfxp.com/xml"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.itfxp.com/xml" elementFormDefault="qualified">
<xsd:element name="students" type="studentsType"/>
<xsd:complexType name="studentsType">
<xsd:sequence>
<xsd:element name="student" type="studentType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="studentType">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="age" type="ageType" />
<xsd:element name="sex" type="sexType" />
</xsd:sequence>
<xsd:attribute name="number" type="numberType" use="required"/>
</xsd:complexType>
<xsd:simpleType name="sexType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="male"/>
<xsd:enumeration value="female"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="ageType">
<xsd:restriction base="xsd:integer">
<xsd:minInclusive value="0"/>
<xsd:maxInclusive value="256"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="numberType">
<xsd:restriction base="xsd:string">
<xsd:pattern value="heima_\d{4}"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>
4.3.2.2 Schema约束引入
1.填写xml文档的根元素
2.引入xsi前缀. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3.引入xsd文件命名空间. xsi:schemaLocation="http://www.itfxp.com/xml student.xsd"
4.为每一个xsd约束声明一个前缀,作为标识 xmlns="http://www.itfxp.com/xml"
<?xml version="1.0" encoding="UTF-8" ?>
<!--
1.填写xml文档的根元素
2.引入xsi前缀. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3.引入xsd文件命名空间. xsi:schemaLocation="http://www.itfxp.com/xml student.xsd"
4.为每一个xsd约束声明一个前缀,作为标识 xmlns="http://www.itfxp.com/xml"
-->
<students
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.itfxp.com/xml"
xsi:schemaLocation="http://www.itfxp.com/xml student.xsd">
<student number="Z0001">
<name>tom</name>
<age>18</age>
<sex>male</sex>
</student>
</students>
五、XML解析
操作xml文档,将文档中的数据读取到内存中
5.1 操作XML两种情况
解析(读取):
将文档中的数据读取到内存中 【实际开发中,我们不做解析】
写入:
将内存中的数据保存到xml文档中。持久化的存储
5.2 常见解析XML方式
5.2.1 sax解析
(Simple API for XML)
处理方式类似于流处理,边读取边解析,采用的是事件回调的方式,书写好用于处理响应事件的方法,进行解析,当进行读取时触发相应事件,执行对应方法
优点:
进行解析时无需加载全部文档,可以边读取边解析
基于事件回调进行响应的解析,只有触发相应事件时才会回调相应方法
可以解析数据量大于内存的数据
缺点:
需要自己维护响应事件的回调方法,随文档的复杂度难度递增
单向解析,不会进行反向查询,只能从头解析
import java.io.InputStream;
import java.util.ArrayList;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
//sax解析xml
public class SaxParse {
public static void main(String[] args) throws Exception {
// 1、调用sax解析工厂对象newInstance方法创建sax解析工厂对象
SAXParserFactory saxFactory = SAXParserFactory.newInstance();
// 2、调用sax工厂对象 newSAXParser方法创建sax解析对象
SAXParser saxParse = saxFactory.newSAXParser();
// 3、创建自定义解析器对象
MySaxHandler mh=new MySaxHandler();
// 4、使用解析对象传入自定义解析器与解析地址解析数据
InputStream is = SaxParse.class.getClassLoader().getResourceAsStream("com/yunhe/day0707/test.xml");
saxParse.parse(is, mh);
// 5、获取数据
ArrayList<Teacher> list = mh.list;
for (Teacher teacher : list) {
System.out.println(teacher);
}
}
}
//sax需要自己创建解析器类解析对应的文件
//创建自定义解析器类继承默认的解析器类
//默认解析器类实现了方法但是没有书写任何方法体
class MySaxHandler extends DefaultHandler{
//startElement
//当读取到起始标签时回调的方法
//endElement
//当读取到结束标签时回调的方法
//characters
//当读取到文本标签时回调的方法
//在sax解析中是按照标签进行解析
//起始标签 结束标签 文本标签
String str="";
//创建集合保存所有teacher对象数据
public ArrayList<Teacher> list=new ArrayList<>();
Teacher t;//保存每次读取数据的teacher对象
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
//当读取到teacher起始标识时创建teacher对象
if(qName.equals("teacher")){
t=new Teacher();
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
//当读取到teacher结束表情时将teacher对象加入集合
if(qName.equals("teacher")){
list.add(t);
}else{
//如果不是对象的结束标签
//判断是否是属性结束标签
if(qName.equals("name")){
t.setName(str);
}else if(qName.equals("age")){
t.setAge(Integer.valueOf(str));
}else if(qName.equals("sex")){
t.setSex(str);
}
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
str=new String(ch,start,length);
}
}
5.2.2 dom解析
(Document Object Model)
解析方式基于数据的节点层次结构进行解析,解析方式类可以理解为内嵌了处理器,在进行加载时使用已经提供的方式进行数据的解析,并以一定的层次结构进行保存,提供相应的方法可以直接进行数据的获取
dom解析采用的是默认处理器预处理形式进行解析,可以理解为执行时会先读取解析一遍数据,并将所有数据按照默认的格式进行存储
优点:
底层以数据节点的形式进行存储数据,提供相应的方法快速获取
可以对某一标签直接进行访问
缺点:
需要加载整个文件,消耗内存,不能处理大于内存的数据
无论是否需要都会加载整个数据
import java.io.InputStream;
import java.util.ArrayList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
//dom解析xml
public class DomParse {
public static void main(String[] args) throws Exception {
//1、使用dom解析工厂类newInstance方法创建dom解析工厂对象
DocumentBuilderFactory DomFactory=DocumentBuilderFactory.newInstance();
//2、使用dom解析工厂对象newDocumentBuilder方法创建dom解析对象
DocumentBuilder domParse = DomFactory.newDocumentBuilder();
//3、使用dom解析对象方法解析指定的流,获取存储当前文档数据的Document对象
InputStream is = SaxParse.class.getClassLoader().getResourceAsStream("com/yunhe/day0707/test.xml");
Document dom = domParse.parse(is);
//4、调用Document类提供的方法从Document对象中获取指定数据
//通过标签名获取指定标签
//会方法当前xml中所有对应标签的对象数据
NodeList teacherList = dom.getElementsByTagName("teacher");
//NodeList 是其自定义的类似于集合的数据容器
ArrayList<Teacher> list=new ArrayList<>();//创建存储数据的集合
for (int i = 0; i < teacherList.getLength(); i++) {
Teacher t=new Teacher();
//获取每个teacher标签
//item获取对应索引标签
Node teacher = teacherList.item(i);
//getChildNodes获取当前标签下的所有子标签
NodeList fieldList = teacher.getChildNodes();
for (int j = 0; j < fieldList.getLength(); j++) {
//获取当前teacher标签中对应的属性标签
Node field = fieldList.item(j);
//获取当前属性标签对应数据的值
if(!field.getNodeName().equals("#text")){
String nodeValue = field.getFirstChild().getNodeValue();
if(field.getNodeName().equals("name")){
t.setName(nodeValue);
}else if(field.getNodeName().equals("age")){
t.setAge(Integer.valueOf(nodeValue));
}else if(field.getNodeName().equals("sex")){
t.setSex(nodeValue);
}
}
}
list.add(t);
}
for (Teacher teacher : list) {
System.out.println(teacher);
}
}
}
5.2.3 jdom解析
(Java-based Document Object Model)
基于java规范开发的dom方式解析xml数据,主要是基于javaAPI与集合修改了原本的nodeList存储节点的形式,与dom原有的API。
JDOM文档声明其目的是“使用20%(或更少)的精力解决80%(或更多)
JDOM自身不包含解析器。它通常使用SAX2解析器来解析和验证输入XML文档(尽管它还可以将以前构造的DOM表示作为输入)。
优点:
将数据存储使用集合的形式存储
简化API的使用
缺点:
没有良好的灵活性
性能较差
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class JDomParse {
//jdom不能使用dom原有的解析
//使用的是sax2的解析
public static void main(String[] args) throws Exception {
//1创建sex解析器new SAXBuilder
SAXBuilder saxBuilder = new SAXBuilder();
//2使用sex解析解析数据build
InputStream resourceAsStream = DomParse.class.getClassLoader().getResourceAsStream("com/yunhe/xml/Students.xml");
Document dom = saxBuilder.build(resourceAsStream);
//3使用jdomAPI进行数据的获取
Element rootElement = dom.getRootElement();//获取代表根节点标签的元素对象
List<Element> studentList = rootElement.getChildren();
ArrayList<Student> list = new ArrayList<>();
for (Element student : studentList) {
Student s = new Student();
List<Element> filedList = student.getChildren();
for (Element filed : filedList) {
String qName = filed.getName();
if (qName.equals("name")) {
s.setName(filed.getValue());
} else if (qName.equals("age")) {
s.setAge(Integer.valueOf(filed.getValue()));
} else if (qName.equals("sex")) {
s.setSex(filed.getValue());
}
// System.out.println(filed.getName() + ":" + filed.getValue());
}
list.add(s);
}
System.out.println(list);
}
}
5.2.4 dom4j解析
DOM4J是一个非常非常优秀的Java XML API,具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件。如今你可以看到越来越多的Java软件都在使用DOM4J来读写XML,为解决jdom简化API导致的灵活性问题,dom4j在其基础上添加了大量的API功能代码
优点:
提高了大量的API应用于各种形式的使用
缺点:
API太过繁琐,如果只是进行简单的解析,不需要使用
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
public class Dom4JParse {
//不能直接使用dom解析器
//需要使用sex的解析器进行解析读取
public static void main(String[] args) throws Exception {
//创建sax解析器对象 new SAXReader
SAXReader saxReader=new SAXReader();
//解析响应的数据read
InputStream resourceAsStream = DomParse.class.getClassLoader().getResourceAsStream("com/yunhe/xml/Students.xml");
Document dom = saxReader.read(resourceAsStream);
//使用dom4j 提供的API进行获取
Element students = dom.getRootElement();//获取根节点元素对象
Iterator studentIterator = students.elementIterator();
ArrayList<Student> list = new ArrayList<>();
while(studentIterator.hasNext()){
Student s = new Student();
Element student = (Element) studentIterator.next();
Iterator fieldIterator = student.elementIterator();
while(fieldIterator.hasNext()){
Element field = (Element)fieldIterator.next();
//System.out.println(field.getName()+":"+field.getStringValue());
String qName = field.getName();
if (qName.equals("name")) {
s.setName(field.getStringValue());
} else if (qName.equals("age")) {
s.setAge(Integer.valueOf(field.getStringValue()));
} else if (qName.equals("sex")) {
s.setSex(field.getStringValue());
}
}
list.add(s);
}
System.out.println(list);
}
}
5.3 常见的XML解析器
JAXP:sun公司提供的解析器,支持dom和sax两种思想
DOM4J:一款非常优秀的解析器
Jsoup:jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。
PULL:Android操作系统内置的解析器,sax方式的。
5.4 Jsoup
5.4.1 Jsoup概述
jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。
5.4.2 Jsoup快速入门
实现步骤
1. 导入jar包
2. 获取Document对象
3. 获取对应的标签Element对象
4. 获取数据
代码实现
//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);
5.4.3 Jsoup常见对象
Document
Jsoup:工具类,可以解析html或xml文档,返回Document
方法名 | 说明 |
parse(File in, String charsetName) | 解析xml或html文件的 |
parse(String html) | 解析xml或html字符串 |
parse(URL url, int timeoutMillis) | 通过网络路径获取指定的html或xml的文档对象 |
getElementById(String id) | 根据id属性值获取唯一的element对象 |
getElementsByTag(String tagName) | 根据标签名称获取元素对象集合 |
getElementsByAttribute(String key) | 根据属性名称获取元素对象集合 |
getElementsByAttributeValue(String key, String value) | 根据对应的属性名和属性值获取元素对象集合 |
/**
* Jsoup对象功能
*/
public class JsoupDemo2 {
public static void main(String[] args) throws IOException {
//2.1获取student.xml的path
String path = JsoupDemo2.class.getClassLoader().getResource("student.xml").getPath();
//2.2解析xml文档,加载文档进内存,获取dom树--->Document
/* Document document = Jsoup.parse(new File(path), "utf-8");
System.out.println(document);*/
//2.parse(String html):解析xml或html字符串
/* String str = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" +
"\n" +
"<students>\n" +
"\t<student number=\"heima_0001\">\n" +
"\t\t<name>tom</name>\n" +
"\t\t<age>18</age>\n" +
"\t\t<sex>male</sex>\n" +
"\t</student>\n" +
"\t<student number=\"heima_0002\">\n" +
"\t\t<name>jack</name>\n" +
"\t\t<age>18</age>\n" +
"\t\t<sex>female</sex>\n" +
"\t</student>\n" +
"\n" +
"</students>";
Document document = Jsoup.parse(str);
System.out.println(document);*/
//3.parse(URL url, int timeoutMillis):通过网络路径获取指定的html或xml的文档对象
URL url = new URL("https://baike.baidu.com/item/jsoup/9012509?fr=aladdin");//代表网络中的一个资源路径
Document document = Jsoup.parse(url, 10000);
System.out.println(document);
}
}
Elements
元素Element对象的集合。可以当做 ArrayList来使用
/**
* Document/Element对象功能
*/
public class JsoupDemo3 {
public static void main(String[] args) throws IOException {
//1.获取student.xml的path
String path = JsoupDemo3.class.getClassLoader().getResource("student.xml").getPath();
//2.获取Document对象
Document document = Jsoup.parse(new File(path), "utf-8");
//3.获取元素对象了。
//3.1获取所有student对象
Elements elements = document.getElementsByTag("student");
System.out.println(elements);
System.out.println("-----------");
//3.2 获取属性名为id的元素对象们
Elements elements1 = document.getElementsByAttribute("id");
System.out.println(elements1);
System.out.println("-----------");
//3.2获取 number属性值为heima_0001的元素对象
Elements elements2 = document.getElementsByAttributeValue("number", "itfxp_0001");
System.out.println(elements2);
System.out.println("-----------");
//3.3获取id属性值的元素对象
Element itcast = document.getElementById("itfxp");
System.out.println(itcast);
}
}
Element
获取子元素对象
方法名 | 说明 |
getElementById(String id) | 根据id属性值获取唯一的element对象 |
getElementsByTag(String tagName) | 根据标签名称获取元素对象集合 |
getElementsByAttribute(String key) | 根据属性名称获取元素对象集合 |
getElementsByAttributeValue(String key, String value) | 根据对应的属性名和属性值获取元素对象集合 |
获取属性值
方法名 | 说明 |
String attr(String key) | 根据属性名称获取属性值 |
获取文本内容
方法名 | 说明 |
String text() | 获取文本内容 |
String html() | 获取标签体的所有内容(包括字标签的字符串内容) |
/**
*Element对象功能
*/
public class JsoupDemo4 {
public static void main(String[] args) throws IOException {
//1.获取student.xml的path
String path = JsoupDemo4.class.getClassLoader().getResource("student.xml").getPath();
//2.获取Document对象
Document document = Jsoup.parse(new File(path), "utf-8");
/*
Element:元素对象
1. 获取子元素对象
* getElementById(String id):根据id属性值获取唯一的element对象
* getElementsByTag(String tagName):根据标签名称获取元素对象集合
* getElementsByAttribute(String key):根据属性名称获取元素对象集合
* getElementsByAttributeValue(String key, String value):根据对应的属性名和属性值获取元素对象集合
2. 获取属性值
* String attr(String key):根据属性名称获取属性值
3. 获取文本内容
* String text():获取所有字标签的纯文本内容
* String html():获取标签体的所有内容(包括子标签的标签和文本内容)
*/
//通过Document对象获取name标签,获取所有的name标签,可以获取到两个
Elements elements = document.getElementsByTag("name");
System.out.println(elements.size());
System.out.println("----------------");
//通过Element对象获取子标签对象
Element element_student = document.getElementsByTag("student").get(0);
Elements ele_name = element_student.getElementsByTag("name");
System.out.println(ele_name.size());
//获取student对象的属性值
String number = element_student.attr("NUMBER");
System.out.println(number);
System.out.println("------------");
//获取文本内容
String text = ele_name.text();
String html = ele_name.html();
System.out.println(text);
System.out.println(html);
}
Node
是Document和Element的父类
六、作业
1、书写工具类传入xml文件与对应类class属性,使用反射将xml数据存入集合并返回
2、自己查看jdom与dom4j相应api方法学习