# 关于键值对

  • 很多编程语言中,会通过 变量名 = 变量值 的格式创建一个变量。因此,序列化数据时,也经常采用这种格式,从而符合程序员的阅读习惯。
    • 这种序列化格式的特点:每行文本包含一个 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 文本中,以下左侧字符需要替换成右侧的转义字符:

    <     &lt;
    >     &gt;
    &     &amp;
    '     &apos;
    "     &quot;
    

# import xml

:Python 的标准库,用于解析或生成 XML 格式的文本。

  • 官方文档 (opens new window)

  • 创建元素:

    >>> 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 对象。
  • 操作元素的属性:

    >>> 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 格式的文本。

  • 官方文档 (opens new window)

  • 安装: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 对象。
  • 操作元素的属性:

    >>> 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 格式的文本。

  • 官方文档 (opens new window)

  • 使用 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 格式的文本。

  • 官方文档 (opens new window)

  • 安装: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}