# 关于键值对
- 很多编程语言中,会通过
变量名 = 变量值
的格式创建一个变量。因此,序列化数据时,也经常采用这种格式,从而符合程序员的阅读习惯。- 这种序列化格式的特点:每行文本包含一个 key 和一个 value 。简称为 key-value pair ,汉语译为键值对。
- 相关历史:
- 20 世纪末,计算机行业主要使用 INI 或类似的序列化格式,因为格式简单。
- 1998 年,W3C 发布了 XML 1.0 标准。它比 INI 的功能更多,能存储数组、字典等数据类型。
- 几年后,业界发明了 JSON 格式。它与 XML 的功能差不多,但更简洁,方便人类阅读。
- 几年后,业界在 JSON 格式的基础上,发明了 YAML 格式。它更方便人类阅读,但不方便程序识别,序列化、反序列化的耗时更久。
# INI
:一种保存键值对数据的文本格式。
- 是 Windows 系统采用的一种配置文件格式,但类 Unix 系统中也存在类似的文件格式,只是没有明确的标准。
# 语法
- INI 格式的文本文件的扩展名为
.ini
。 - 用分号
;
声明单行注释,有的解析器也支持用#
。 - 每行声明一对键值对参数,用等号
=
连接 key 和 value 。- key 、 value 都是字符串类型,有的解析器也能识别 true、false 等特殊值。
- key 、 value 前后的空格会被忽略,除非用双引号作为定界符包住。
- 用中括号
[ ]
声明一个段(Section)。- 中括号内的字符串会作为段名,不会忽略空格。
- 每个段中, key 不能重复。
# 例
[Section 1]
id = 1
tips = Hello World
[MySQL]
runner = root
work_dir = /opt/mysql
# XML
:可扩展标记语言(X Extensive Markup Language),一种序列化数据的文本格式。
- 由于 HTML 不支持自定义标签,通用性低,不适合保存结构化数据。1998 年,W3C 发布了 XML 1.0 标准,使用与 HTML 标签相似的语法来保存数据。
- 虽然 HTML、XML 都属于文本格式,但 HTML 是用于将数据显示成富文本,方便人类查看。而 XML 是用于将数据序列化,方便传输、存储。
# 语法
XML 格式的文本文件的扩展名为
.xml
。区分大小写。
空格、换行符不会被忽略。
用
<!--
与-->
声明注释。必须在第一行声明 XML 的版本:
<?xml version='1.1' encoding='UTF-8'?>
XML 以元素为单位存储数据。
- 每个元素是用一对标签包住一个值,格式如下:
<tag>value</tag>
- 标签用尖括号标记。
- 标签、值都是字符串,但是不必加引号作为定界符。
- 每个元素有两个标签,前一个标签称为开始标签,前一个标签称为结束标签。如果元素没有值,则可以合并成一个标签。如下:
<name /> <name id='1' />
- 可以在开始标签中加上属性。属性是键值对格式,且它的值必须要用双引号作为定界符。如下:
<note date="20201001" time="12:00"> Hello World </note>
- 一个元素的值可以是一个或多个子元素,构成嵌套的层级结构。如下:
<student> <id>1</id> <name>one</name> </student>
- 一个 XML 文档中,最外层的元素称为根元素。
- 每个元素是用一对标签包住一个值,格式如下:
在 XML 文本中,以下左侧字符需要替换成右侧的转义字符:
< < > > & & ' ' " "
# import xml
:Python 的标准库,用于解析或生成 XML 格式的文本。
创建元素:
>>> from xml.etree import ElementTree >>> root = ElementTree.Element('test') # 创建一个元素 >>> root <Element 'test' at 0x7fe42c2d1e08> >>> root.tag # 获取元素的标签名(str 类型) 'test' >>> root.text = 'Hello World' # 设置元素的值(str 类型) >>> root.text 'Hello World'
- 将 XML 元素映射为
xml.etree.ElementTree.Element
对象。
- 将 XML 元素映射为
操作元素的属性:
>>> root.set('date', '20191001') # 设置一个属性(如果该属性已存在则覆盖其值) >>> root.set('time', '12:00') >>> root.attrib # 获取元素的全部属性(dict 类型) {'date': '20191001', 'time': '12:00'}
操作子元素:
>>> sub = ElementTree.Element('sub') >>> root.append(sub) # 添加一个子元素(可以添加多个) >>> root.append(sub) >>> root[:] # Element 对象支持 list 的大部分操作方法 [<Element 'sub' at 0x7efd36b9a278>, <Element 'sub' at 0x7efd36b9a278>] >>> root.find('sub') # 根据 tag 名查找一个子元素,返回第一个匹配的,没找到则返回 None <Element 'sub' at 0x7fe4247fb278> >>> root.findall('sub') # 根据 tag 名查找所有匹配的子元素,没找到则返回[] [<Element 'sub' at 0x7fe4247fb278>, <Element 'sub' at 0x7fe4247fb278>] >>> root.remove(sub) # 删除一个子元素 >>> root.clear() # 删除 text、子元素、attrib
与字符串的转换:
>>> root = ElementTree.XML('<test />') # 解析 XML 格式的字符串(返回的是根元素) >>> root <Element 'test' at 0x7fe42c2d1e08>
>>> ElementTree.tostring(root, encoding='utf-8') # 生成 XML 格式(返回的是 bytes 类型) b'<test />'
# import lxml
:Python 的第三方库,用于解析或生成 XML、HTML 格式的文本。
安装:
pip install lxml
lxml 的用法与标准库 xml 相似,且功能更强,比如支持自动缩进。
创建元素:
>>> from lxml import etree >>> root = etree.Element('test') # 创建一个元素 >>> root <Element test at 0x243576e9d40> >>> root.tag # 获取元素的标签名(str 类型) 'test' >>> root.text = 'Hello World' # 设置元素的值(str 类型) >>> root.text 'Hello World'
- 将 XML 元素映射为
lxml.etree.Element
对象。
- 将 XML 元素映射为
操作元素的属性:
>>> root.set('date', '20191001') # 设置一个属性(如果该属性已存在则覆盖其值) >>> root.set('time', '12:00') >>> root.attrib # 获取元素的全部属性(dict 类型) {'date': '20191001', 'time': '12:00'}
操作子元素:
>>> sub = etree.Element('sub') >>> root.append(sub) # 添加一个子元素(可以添加多个) >>> root.append(sub) >>> root[:] # Element 对象支持 list 的大部分操作方法 [<Element sub at 0x243576e9e40>] # 重复添加同一个 Element 对象作为子元素时,只会生效一次
与字符串的转换:
>>> root = etree.XML('<root><id>1</id></root>') # 解析 XML 格式的字符串(返回的是根元素) >>> root <Element root at 0x243573c3ac0>
>>> etree.tostring(root, encoding='utf-8') # 生成 XML 格式(返回的是 bytes 类型) b'<root><id>1</id></root>' >>> etree.tostring(root, encoding='utf-8', xml_declaration=True) # 开头加上声明语句 b"<?xml version='1.0' encoding='utf-8'?>\n<root><id>1</id></root>"
加上缩进:
>>> root.text # 处理前,元素值为空 >>> etree.indent(root, space=' ') >>> root.text # 处理后,元素值加上了缩进 '\n ' >>> print(etree.tostring(root, encoding='utf-8').decode()) <root> <id>1</id> </root>
处理 HTML 格式:
>>> root = etree.XML('<html><head/><body><p>Hello<br/>World</p></body></html>') >>> etree.tostring(root, method='xml') b'<html><head/><body><p>Hello<br/>World</p></body></html>' >>> etree.tostring(root, method='html') b'<html><head></head><body><p>Hello<br>World</p></body></html>' # HTML 标准的标签有些许差异
# JSON
:JSON(JavaScript Object Notation),一种序列化数据的文本格式。
- JSON 原本是 JavaScript 编程语言中专用的数据格式,后来演变为一种通用的文本格式,常用于在不同编程语言、不同软件之间传递数据。
# 语法
- JSON 格式的文本文件的扩展名为
.json
。 - JavaScript 中可以用
//
声明单行注释,但 JSON 中不允许使用注释,否则会导致解析出错。 - JSON 中可以记录多个键值对(key-value)格式的字段,每个键值对之间用逗号分隔。
- key 必须是字符串类型,用双引号包住,且同一作用域下不能重名。
- key 与 value 之间用冒号
:
连接。 - 例如,JSON 中的字段
"name": "Hello"
相当于 JavaScript 中的变量name = "Hello"
。 - value 可以是以下数据类型:
number # 数值,取值为整数或浮点数 boolean # 布尔,取值为小写的 true 或 false string # 字符串,用双引号作为定界符 array # 数组,用中括号包住,有序地存储多个键值对 object # 对象,用花括号包住,无序地存储多个键值对 null # 空,代表 value 不存在
- 为了方便阅读,JSON 文本通常采用 4 个空格作为缩进。
- 字段之间可以插入任意个空字符,不影响语法。
- 例:
{ "name": "Leo", "age": 12, "emails": [ "[email protected]", "[email protected]" ] }
# import json
:Python 的标准库,用于生成、解析 JSON 格式的文本。
使用 json.dumps() 可将 Python 基本数据类型的对象转换成 JSON 中的数据类型。转换关系如下:
int, long, float —> number str, unicode —> string list, tuple —> array dict —> object True —> true False —> false None —> null
例:JSON 序列化
>>> import json >>> dic1 = {'a': 1, 'b': True} >>> json.dumps(dic1) '{"a": 1, "b": true}' >>> print(json.dumps(dic1, indent=4, sort_keys=True)) # 默认 indent=None ,在输出时不换行、不缩进。这里设置 4 个空格的缩进,并对 key 排序 { "a": 1, "b": true }
>>> json.dumps(json) # 非基本数据类型的对象,在序列化时会报错 TypeError: Object of type module is not JSON serializable >>> json.dumps('你好') # Unicode 字符会按 str.encode('unicode_escape') 编码 '"\\u4f60\\u597d"' >>> json.dumps("你好", ensure_ascii=False) # 取消将输出的字符全部转换成 ASCII 码,从而保留 Unicode 字符 '"你好"'
例:JSON 反序列化
>>> json.loads('true') True >>> json.loads('{"你好": 1}') {'你好': 1}
# YAML
:一种序列化数据的文本格式。与 JSON 相似,但更简洁。
- 发音为
/ˈjæməl/
。
# 语法
YAML 格式的文本文件的扩展名为
.yaml
或.yml
。区分大小写。
用
#
声明单行注释。必须用缩进表示层级关系。
- 每层缩进通常为 2 个空格,不支持 Tab 等空字符。
- 缩进的空格数可以任意,但同一层级的空格数要相同。
可以在一个文件中保存多个 YAML 文档,每个文档用一行
---
表示开头、用一行...
表示结尾。- 也可只在每个文档之间用一行
---
分隔。
- 也可只在每个文档之间用一行
例:
debug: true env: test server: # value 为数组类型 - 10.0.0.1 - 10.0.0.2 environment: # value 为字典类型 var1: 1 var2: Hello
YAML 中可以记录多个键值对(key-value)格式的字段。
- key 必须是字符串类型,且同一作用域下不能重名。
- key 与 value 之间用
:
连接。- 注意冒号 : 右侧必须存在空格,否则冒号左右的内容会被看作一个字符串。
- value 为字符串类型时,可以采用单引号、双引号作为定界符,也可以省略。
- value 为数组类型时,有两种写法:
env: ['dev', 'test']
env: - dev # 数组元素多一层缩进,每个元素以 - 开头 - test
- value 为空时,写作
null
或~
。为空字符串时,写作''
或""
。
# import yaml
:Python 的第三方库,用于生成、解析 YAML 格式的文本。
安装:
pip install PyYAML
例:序列化
>>> import yaml >>> dic1 = {'a': 1, 'b': True} >>> yaml.dump(dic1) 'a: 1\nb: true\n'
>>> yaml.dump('你好') # Unicode 字符会按 str.encode('unicode_escape') 编码 '"\\u4F60\\u597D"\n' >>> yaml.dump('你好', allow_unicode=True) # 保留 Unicode 字符 '你好\n...\n'
- 序列化时,默认会将 Python 字典的 key 按升序排列。
例:反序列化
>>> yaml.load('a: 1\nb: true\n', Loader=yaml.CLoader) {'a': 1, 'b': True}