XML知识整理

2023-01-09

你是否有很多xml放不下?做人要潇洒一点。喜欢C语言,未必要写它一辈子的。我喜欢html,未必要给它写好看的css。我看不懂xml,难道直接去叫二进制过来,你让我反编译一下。诶,这个好像还真可以。

咳咳,总之,这是一篇简单的关于xml的博客。

​ 总能在各个项目里看到xml的身影,我的印象中xml是个长得和html差不多的玩意,可具体他是什么又说不清楚。于是打算系统学习下xml的相关知识。以及本篇文章不会讨论语法细节,主要围绕作用功能做整理(大概标题可以换成:如何看懂xml?。这篇博客权当是一个学习笔记吧。

概念与本质

XML 指可扩展标记语言(EXtensible Markup Language),是一种用于存储、传输和重建任意数据的标记语言和文件格式。它定义了一组规则,用于以人类可读和机器可读的格式对文档进行编码。万维网联盟 1998年XML 1.0 规范其他几个相关规范——它们都是免费的开放标准——定义了 XML。

​ 以上来自维基百科。

​ w3school有两句话对我可谓醍醐灌顶,一语惊醒梦中人。

  • HTML 被设计用来显示数据。
  • XML 被设计用来传输和存储数据。

基础

xml自我描述

​ xml可以非常简单的用一个标签描述自己,最常见的应该如下所示:

​ 这句经常出现在文件开头的一行代码表示这个文件是xml文件,版本为1.0,编码方式为UTF-8

<?xml version="1.0" encoding="UTF-8"?>

xml元素与属性

​ 让我们嫖个例子来说明:

​ 下面例子中的data就是属性,而notetofrom等等这些则是元素。属性提供关于元素的额外(附加)信息。如果学习过同为标记语言的html则很好理解,但是xml的标签是完全的可扩展可自定义的。

​ 元素支持嵌套,嵌套的元素组成了整个xml文档的文档树。如果有写过针对html的爬虫应该很好明白,针对html的爬虫就可以根据html文档树节点进行遍历来获取网页的前端内容。例子中的note元素就是文档树的根节点,他的孩子包括tofromheadingbody,通过这样的结构就可以了解整个xml文档的内容。

​ 从这个文档中我们可以很容易知道,他想传输/存储的数据是一个2008年8月8日写下的便条,便条的内容包括标题、内容、书写方和接收方。

<note date="08/08/2008">
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>

​ 但是这个代码的写法并不够好。之前说过XML被设计用来传输和存储数据。无论是为了方便扩展维护,方便不同的系统、程序对数据进行读取还是方便以嵌套形式展现结构,都应该把数据写在元素内而不是标签内的属性中。便条的日期本身也是十分重要的数据内容。把它写成元素会更好,如下所示:

<note>
<date>
  <day>08</day>
  <month>08</month>
  <year>2008</year>
</date>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>

​ 如果我们作为开发者,需要使用xml进行数据存储/传输时,也应保持这样的良好习惯。

浏览器与xml

​ 在我年少无知的时候,我曾以为,所有的.*ml文件拖到浏览器里,都会像html一样华丽的展示出来,但事实证明我错了。比如xml,你拖进来,它只会显示原本的文本格式文件。因为XML文档不携带有关如何显示数据的信息。他的标签是自定义的,浏览器没法认得,所以一般的浏览器都会显示为源代码。

​ 当然,也有办法以特殊的格式在浏览器显示xml,就是使用css文件,不过对于被设计用来传输数据的xml来说,这大概没什么意义。

过渡

​ 好了,你现在已经明白了xml的本质和基础知识,是一个成熟的xml reader了。下面是一个从AMI公司的redfish相关xml里截取出来的一部分代码。相信你一定能很容易的看懂它吧?

<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">

  <edmx:Reference Uri="http://docs.oasis-open.org/odata/odata/v4.0/errata03/csd01/complete/vocabularies/Org.OData.Core.V1.xml">
    <edmx:Include Namespace="Org.OData.Core.V1" Alias="OData"/>
  </edmx:Reference>
  <edmx:Reference Uri="http://docs.oasis-open.org/odata/odata/v4.0/errata03/csd01/complete/vocabularies/Org.OData.Capabilities.V1.xml">
    <edmx:Include Namespace="Org.OData.Capabilities.V1" Alias="Capabilities"/>
  </edmx:Reference>
  <edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/Resource_v1.xml">
    <edmx:Include Namespace="Resource"/>
    <edmx:Include Namespace="Resource.v1_0_0"/>
  </edmx:Reference>
  
</edmx:Edmx>

反正我当时的感想就是,这都什么玩意

进阶

命名空间

​ 上面提到了xml里的标签名是完全可自定义的。那就涉及到了一个问题:命名冲突。在同一个xml里,我想给一个标签起名叫table来表示桌子,一个标签起名也叫做table来表示定义一个表格(这是html中的table含义),该怎么办呢?

增加前缀

​ 最容易想到的办法自然是为不同的名字划分各自的“定义域”。具体表现为为他们添加前缀。这里还是嫖个例子来说明,如下所示:

​ h前缀的table表示html中的表格的意思

<h:table>
   <h:tr>
   <h:td>Apples</h:td>
   <h:td>Bananas</h:td>
   </h:tr>
</h:table>

​ f前缀的table表示这是一个桌子

<f:table>
   <f:name>African Coffee Table</f:name>
   <f:width>80</f:width>
   <f:length>120</f:length>
</f:table>

​ 可以简单的理解为同一个前缀就是同一个命名空间。

命名空间

​ xml存在namespace属性来定义命名空间,即xmlns

​ 当命名空间被定义在元素的开始标签中时,所有带有相同前缀的子元素都会与同一个命名空间相关联。

​ 使用如下语法:

xmlns:namespace-prefix="namespaceURI"

​ 我们可以注意到,引号内的是一个命名空间的URI,但为什么这里要放一个URI呢?

用于标示命名空间的地址不会被解析器用于查找信息。其惟一的作用是赋予命名空间一个惟一的名称。不过,很多公司常常会作为指针来使用命名空间指向实际存在的网页,这个网页包含关于命名空间的信息。

​ 下面看一个命名空间的例子以及之前的过渡部分的代码(这回是不是好懂多了?):

<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>
<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">

  <edmx:Reference Uri="http://docs.oasis-open.org/odata/odata/v4.0/errata03/csd01/complete/vocabularies/Org.OData.Core.V1.xml">
    <edmx:Include Namespace="Org.OData.Core.V1" Alias="OData"/>
  </edmx:Reference>
  <edmx:Reference Uri="http://docs.oasis-open.org/odata/odata/v4.0/errata03/csd01/complete/vocabularies/Org.OData.Capabilities.V1.xml">
    <edmx:Include Namespace="Org.OData.Capabilities.V1" Alias="Capabilities"/>
  </edmx:Reference>
  <edmx:Reference Uri="http://redfish.dmtf.org/schemas/v1/Resource_v1.xml">
    <edmx:Include Namespace="Resource"/>
    <edmx:Include Namespace="Resource.v1_0_0"/>
  </edmx:Reference>
  
</edmx:Edmx>

默认命名空间

​ 在使用命名空间的时候,每一个标签的前面都要加一个冒号,还有前缀名,写起来是否有些过于麻烦了呢?

​ 所以有了默认命名空间,在元素的开始标签中进行声明之后,所有的子元素与该元素为同一命名空间,省略了命名空间的名称

​ 默认命名空间使用如下语法:

xmlns="namespaceURI"

​ 例子如下:

<table xmlns="http://www.w3.org/TR/html4/">
   <tr>
   <td>Apples</td>
   <td>Bananas</td>
   </tr>
</table>

CDATA

​ 如果看到了CDATA,不用惊慌,CDATA十分简单。CDATA 部分由 “<![CDATA[” 开始,由 “]]>” 结束,其中的所有内容都会被解析器忽略。

参考

https://www.w3school.com.cn/xml/index.asp