XML简介
网上有很多优秀的XML教程,想学习详细知识的同学可以去搜索学习一下,比如W3CSchool网站(链接: W3CSchool XML教程.)本文就是我自己在W3CSchool学习总结的一篇文章。
一、什么是XML?
- XML:可扩展标记语言(eXtensible Markup Language),这名称不用记没啥用,知道可扩展就行。
二、XML是干什么的?
- 很简单就是设计用来存储和传输数据的。
三、XML特点,和HTML对比
XML | HTML |
---|---|
可扩展标记语言 | 超文本标记语言 |
标签自定义 | 标签不可自定义 |
用来存储和传输数据重点在于数据内容 | 用来显示数据,重点在于显示的外观 |
用来存储和传输数据 | 用来显示数据 |
树结构 | 树结构 |
四、XML用途
-
简化了数据的共享和传输
可以理解XML就是给数据提供了一个标准规范,让数据按照XML的标准保存和传输。然后各个平台就可以通过用XML的标准去处理这些数据,从而达到数据可以跨平台共享。
-
使数据更独立。
XML数据被保存在本地,可以直接被访问和修改。相当于脱离了各个平台的业务逻辑,完全解耦脱离出来,处理起来更灵活。
-
可以被用来创建新的语言。
因为他的可扩展性,标签可以自己定义,所以也可以自己创建一门自己的语言。比如XHTML。
五、XML语法
具体语法可以参考链接:XML语法.
- 必须有关闭标签。HTML中可以没有。
- XML标签必须正确嵌套。
- 对大小写敏感,所以不要太随意了,平时就养成准确使用大小写的习惯最好。
- 必须有根元素。树结构的,树没有根怎么长是吧!
- 属性值必须用引号包裹,单引号还是双引号看实际使用。
- 某些特殊字符需要使用其他字符代替,比如小于号‘<’需要用 < 表示,具体还有哪些使用的时候查即可。记不住啊…
- 注释语法和HTML差不多一样一样的,
<!-- 这是一行注释 -->
- 空格不会被删减,换行用LF。
- 标签命名规则:链接:XML标签命名规则
六、DTD 、XML Schema
- DTD全称为,Document Type Definition,中文翻译为文档类型定义,就是一套规则,用来定义如何创建标记语言的标签,一套标签的定义规则。
- XML Schema 作用是定义 XML 文档的合法构建模块,类似 DTD。就是和DTD是同样的东西,只不过是只能用于定义XML用的,用来替代DTD来定义XML。
- DTD教程
- XML Schema教程
七、XML命名空间
- 规则:xmlns:前缀=“URI”。
- 命名空间是用来解决元素名称冲突的。
- 命名空间定义在元素的开始标签者根标签的 xmlns 属性中
- 根标签中使用命名空间:
<root xmlns:h="http://www.w3.org/TR/html4/"
xmlns:f="//www.w3cschool.cn/furniture">
<h:table>
<h:tr>
<h:td>Apples</h:td>
<h:td>Bananas</h:td>
</h:tr>
</h:table>
<f:table>
<f:name>African Coffee Table</f:name>
<f:width>80</f:width>
<f:length>120</f:length>
</f:table>
</root>
- 开始标签中使用命名空间:
<root>
<h:table xmlns:h="http://www.w3.org/TR/html4/">
<h:tr>
<h:td>Apples</h:td>
<h:td>Bananas</h:td>
</h:tr>
</h:table>
<f:table xmlns:f="//www.w3cschool.cn/furniture">
<f:name>African Coffee Table</f:name>
<f:width>80</f:width>
<f:length>120</f:length>
</f:table>
</root>
上面的例子就是出现了相同名称的元素标签
所以通过在根标签或者开始标签添加命名空间来标识区分标签。- 注释:命名空间中的URL不会被解释器用来查找信息。目的只是用来给命名空间一个唯一的标识。不过,平常使用的时候都会作为把URL当做指针来使用,命名空间指向实际存在的网页活资源,这个网页或者资源包含关于命名空间的信息。
八、XML解析
解析XML,就是更具XML的树形结构对XML文档进行增删改查的操作。可以理解为利用某种API对XML文档进行编辑操作。
8.1、DOM解析
基于树形结构,和文档驱动,需要解析前先把xml数据保存到内存中。
- 什么是DOM
DOM(Document Object Model 文档对象模型)定义了访问和操作文档的标准方法。 - XML DOM
(1)定义了访问和操作XML文档的标准方法。
(2)XML DOM把XML文档当中树形结构来看待,可以利用定义好的标准方法,对XML中的元素进行删除、增加、查询和修改操作。元素以及他们的属性都被称为节点。 - Android 中DOM解析XML(其他JS中进行解析请参考:JS中进行DOM解析里面有相关代码,或者百度去吧,我也不会…)
xmlTest.xml文件(放入Android的assets文件夹下)代码如下:
<?xml version="1.0" encoding="utf-8"?>
<books>
<book id="1">
<name>钢铁是怎样练成的</name>
<author>尼古拉.奥斯特洛夫斯基</author>
</book>
<book id="2">
<name>三国演义</name>
<author>罗贯中</author>
</book>
<book id="3">
<name>水浒传</name>
<author>施耐庵</author>
</book>
</books>
解析代码如下:
public void getXMLData() throws IOException {
InputStream inputStream = null;
try {
inputStream = this.getAssets().open("xmlTest.xml");
//第一步,创建DocumentBuilderFactory工厂对象
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
//第二步,通过工厂对象创建DocumentBuilder建造者
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
//第三步,创建Document存放整个xml的数据
Document document = documentBuilder.parse(inputStream);
//第四步,获取xml根节点
Element element = document.getDocumentElement();
//第五步,使用根节点和子节点名称获取所有子节点为‘book’的节点
NodeList bookList = element.getElementsByTagName("book");
//第一种方式通过Element和具体标签名获取值
for (int i = 0; i < bookList.getLength(); i++) {
//获取某个book标签元素
Element book = (Element) bookList.item(i);
//获取标签的id属性,并显示到TextView上
mTextView.append(book.getAttribute("id"));
//更具标签名获取name标签信息
NodeList bookName = book.getElementsByTagName("name");
//因为name没有子节点了所以省略了遍历直接取了第一个值,拿到name节点
Node item = bookName.item(0);
//获取name值并显示到TextView上
mTextView.append("书名:" + item.getTextContent() + "\n");
NodeList author = book.getElementsByTagName("author");
Node authorItem = author.item(0);
mTextView.append("作者:" + authorItem.getTextContent() + "\n\n");
}
//添加分隔线
mTextView.append("\n --------------------------------------------------\n\n");
//第二种方式通过Node节点获取值
for (int i = 0; i < bookList.getLength(); i++) {
//获取某个book标签节点
Node book = bookList.item(i);
//获取标签的属性列表,并显示到TextView上
NamedNodeMap attributes = book.getAttributes();
Node id = attributes.getNamedItem("id");
mTextView.append(id.getTextContent());
//获取book节点的子节点
NodeList bookInfos = book.getChildNodes();
for (int j = 0; j < bookInfos.getLength(); j++) {
//获取某个子节点
Node bookInfo = bookInfos.item(j);
//判断节点名
if ("name".equals(bookInfo.getNodeName())) {
//获取相应节点数据
String name = bookInfo.getTextContent();
//获取name标签值
mTextView.append("书名:" + name + "\n");
} else if ("author".equals(bookInfo.getNodeName())) {
String author = bookInfo.getTextContent();
mTextView.append("作者:" + author + "\n\n");
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}finally {
inputStream.close();
}
}
不要忘记在main_activity.xml中添加个TextView哦!
8.2 、SAX解析
- SAX(Simple API for XML),基于事件流驱动的XML解析方法。
- SAX解析,主要是实现自己的Handler接口(DefaultHandler)。
- 不可以主动控制处理事件的结束。
- 解析代码如下:
public void getSAXData() {
try {
//第一步,创建SAX解析工厂
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
//第二步,SAX工厂生产创建SAX解析器
SAXParser saxParser = saxParserFactory.newSAXParser();
//第三步,创建SAX文档处理者
MyXMLHandler myXMLHandler = new MyXMLHandler();
//第四步,获取xml流
InputStream inputStream = this.getAssets().open("xmlTest.xml");
//第五步,解析
saxParser.parse(inputStream, myXMLHandler);
inputStream.close();
//第六步,获取解析完的数据
ArrayList<Book> books = myXMLHandler.getBooks();
for (int i = 0; i < books.size(); i++) {
mTextView.append(books.get(i).getId());
mTextView.append(".");
mTextView.append("书名:"+books.get(i).getName());
mTextView.append("\n");
mTextView.append("作者:"+books.get(i).getAuthor());
mTextView.append("\n\n");
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
8.3、PULL解析
- PULL(),同SAX基于事件流的一种XML解析方式。
- 可以主动控制事件的结束。
- 具体解析过程如下:
private void getPULLData() {
List<Book> books = null;
Book book = null;
try {
//第一步,获取解析器
XmlPullParser parser = Xml.newPullParser();
//第二步,获取数据
InputStream inputStream = this.getAssets().open("xmlTest.xml");
parser.setInput(inputStream, "UTF-8");
//第三步,获取标识类型
int type = parser.getEventType();
//如果等于结束标识,停止解析
while (type != XmlPullParser.END_DOCUMENT) {
switch (type) {
//文档开始事件
case XmlPullParser.START_DOCUMENT:
//初始化books列表
books = new ArrayList<Book>();
break;
//解析开始事件
case XmlPullParser.START_TAG:
//判断标签元素是否是book
if ("book".equals(parser.getName())) {
book = new Book();
//得到book标签的属性值,并设置book的id
book.setId(parser.getAttributeValue(0));
}
if (book != null) {
//判断标签元素是否是name
if ("name".equals(parser.getName())) {
book.setName(parser.nextText());
//判断开始标签元素是否是author
} else if ("author".equals(parser.getName())) {
book.setAuthor(parser.nextText());
}
}
break;
//结束事件
case XmlPullParser.END_TAG:
if ("book".equals(parser.getName())) {
//将book添加到books集合
books.add(book);
book = null;
}
break;
default:
}
//下一个元素并触发相应事件
type = parser.next();
}//end while
inputStream.close();
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
for (int i = 0; i < books.size(); i++) {
mTextView.append(books.get(i).getId());
mTextView.append(".");
mTextView.append("书名:" + books.get(i).getName());
mTextView.append("\n");
mTextView.append("作者:" + books.get(i).getAuthor());
mTextView.append("\n\n");
}
}
- 完整的Activity代码:
package com.easy.xmltest;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Xml;
import android.widget.TextView;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
private Book book;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = findViewById(R.id.text);
//try {
// getXMLData();
//} catch (IOException e) {
// e.printStackTrace();
//}
//getSAXData();
getPULLData();
}
private void getPULLData() {
List<Book> books = null;
Book book = null;
try {
//第一步,获取解析器
XmlPullParser parser = Xml.newPullParser();
//第二步,获取数据
InputStream inputStream = this.getAssets().open("xmlTest.xml");
parser.setInput(inputStream, "UTF-8");
//第三步,获取标识类型
int type = parser.getEventType();
//如果等于结束标识,停止解析
while (type != XmlPullParser.END_DOCUMENT) {
switch (type) {
//文档开始事件
case XmlPullParser.START_DOCUMENT:
//初始化books列表
books = new ArrayList<Book>();
break;
//解析开始事件
case XmlPullParser.START_TAG:
//判断标签元素是否是book
if ("book".equals(parser.getName())) {
book = new Book();
//得到book标签的属性值,并设置book的id
book.setId(parser.getAttributeValue(0));
}
if (book != null) {
//判断标签元素是否是name
if ("name".equals(parser.getName())) {
book.setName(parser.nextText());
//判断开始标签元素是否是author
} else if ("author".equals(parser.getName())) {
book.setAuthor(parser.nextText());
}
}
break;
//结束事件
case XmlPullParser.END_TAG:
if ("book".equals(parser.getName())) {
//将book添加到books集合
books.add(book);
book = null;
}
break;
default:
}
//下一个元素并触发相应事件
type = parser.next();
}//end while
inputStream.close();
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
for (int i = 0; i < books.size(); i++) {
mTextView.append(books.get(i).getId());
mTextView.append(".");
mTextView.append("书名:" + books.get(i).getName());
mTextView.append("\n");
mTextView.append("作者:" + books.get(i).getAuthor());
mTextView.append("\n\n");
}
}
public void getSAXData() {
try {
//第一步,创建SAX解析工厂
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
//第二步,SAX工厂生产创建SAX解析器
SAXParser saxParser = saxParserFactory.newSAXParser();
//第三步,创建SAX文档处理者
MyXMLHandler myXMLHandler = new MyXMLHandler();
//第四步,获取xml流
InputStream inputStream = this.getAssets().open("xmlTest.xml");
//第五步,解析
saxParser.parse(inputStream, myXMLHandler);
inputStream.close();
//第六步,获取解析完的数据
ArrayList<Book> books = myXMLHandler.getBooks();
for (int i = 0; i < books.size(); i++) {
mTextView.append(books.get(i).getId());
mTextView.append(".");
mTextView.append("书名:" + books.get(i).getName());
mTextView.append("\n");
mTextView.append("作者:" + books.get(i).getAuthor());
mTextView.append("\n\n");
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void getXMLData() throws IOException {
InputStream inputStream = null;
try {
inputStream = this.getAssets().open("xmlTest.xml");
//第一步,创建DocumentBuilderFactory工厂对象
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
//第二步,通过工厂对象创建DocumentBuilder建造者
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
//第三步,创建Document存放整个xml的数据
Document document = documentBuilder.parse(inputStream);
//第四步,获取xml根节点
Element element = document.getDocumentElement();
//第五步,使用根节点和子节点名称获取所有子节点为‘book’的节点
NodeList bookList = element.getElementsByTagName("book");
//第一种方式通过Element和具体标签名获取值
for (int i = 0; i < bookList.getLength(); i++) {
//获取某个book标签元素
Element book = (Element) bookList.item(i);
//获取标签的id属性,并显示到TextView上
mTextView.append(book.getAttribute("id"));
//更具标签名获取name标签信息
NodeList bookName = book.getElementsByTagName("name");
//因为name没有子节点了所以省略了遍历直接取了第一个值,拿到name节点
Node item = bookName.item(0);
//获取name值并显示到TextView上
mTextView.append("书名:" + item.getTextContent() + "\n");
NodeList author = book.getElementsByTagName("author");
Node authorItem = author.item(0);
mTextView.append("作者:" + authorItem.getTextContent() + "\n\n");
}
//添加分隔线
mTextView.append("\n --------------------------------------------------\n\n");
//第二种方式通过Node节点获取值
for (int i = 0; i < bookList.getLength(); i++) {
//获取某个book标签节点
Node book = bookList.item(i);
//获取标签的属性列表,并显示到TextView上
NamedNodeMap attributes = book.getAttributes();
Node id = attributes.getNamedItem("id");
mTextView.append(id.getTextContent());
//获取book节点的子节点
NodeList bookInfos = book.getChildNodes();
for (int j = 0; j < bookInfos.getLength(); j++) {
//获取某个子节点
Node bookInfo = bookInfos.item(j);
//判断节点名
if ("name".equals(bookInfo.getNodeName())) {
//获取相应节点数据
String name = bookInfo.getTextContent();
//获取name标签值
mTextView.append("书名:" + name + "\n");
} else if ("author".equals(bookInfo.getNodeName())) {
String author = bookInfo.getTextContent();
mTextView.append("作者:" + author + "\n\n");
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} finally {
inputStream.close();
}
}
}
8.4、三种解析对比
解析方式 | 原理 | 优点 | 缺点 | 使用场景 |
---|---|---|---|---|
DOM解析 | 基于树形结构,基于文档驱动,需要提前把文档缓存到内存中 | 1.耗内存,因为需要提前保存到内存中,所以比较消耗内存。2.耗时间,解析前需要先把文档解析成树形结构所以比较耗时间 | 耗内存,耗时间 | 1.XML文档小。2.操作文档频繁。3.可能会修改xml |
SAX解析 | 基于事件流驱动,按需解析 | 1.速度快,从开始往下解析,遇到相应的开始标签或者结束标签直接就可以返回当前的值,不需要全部解析完再取值。2.灵活性高,可以根据API灵活解析。3.占用内存少。 | 1.解析复杂,API复杂。2.扩展性差,不能更改XML | 1.XML文档大。2.不需要修改文档。3.不需要多次访问文档。不需要控制事件停止时机。 |
PULL解析 | 基于事件流驱动,按需解析 | 1.速度快,从开始往下解析,遇到相应的开始标签或者结束标签直接就可以返回当前的值,不需要全部解析完再取值。2.灵活性高,可以根据API灵活解析。3.占用内存少。4.可控制事件结束的时机,比SAX更简单。 | 扩展性差,不能更改XML | 1.解析复杂,API复杂。2.扩展性差,不能更改XML |