.py

直接使用python存储配置信息,通常使用字典数据类型。

# python
{
    'id': 1,
    'name': "Jason",
    'human': True,
    'hobbies': [
        'eating',
        'playing',
    ],
    'features': {
        'assets': None,
        'weight': 60,
    },
}
{
    "id": 1,
    "name": "Jason",
    "human": true,
    "hobbies": [
        "eating",
        "playing"
    ],
    "features": {
        "assets": null,
        "weight": 60
    }
}

.ini

INI

INI(Initialization File)是Windows系统中最常见的配置文件格式,它使用键值对的形式存储配置信息。通常只支持字符串类型,虽然一些解析器可能支持其他类型,但这不是标准的INI格式。不支持复杂的数据结构,扩展性较差,不适合表示复杂或层次深的数据。

INI语法细节

INI文件由节(sections)、键(keys)和值(values)组成。节由方括号包围的节名表示,键和值成对出现,用等号或冒号分隔。

[Profile]
name=Jason
age=18
hobbies=eating,playing
{
    "Profile": {
        "name": "Jason",
        "age": 18,
        "hobbies": "eating,playing"
    }
}

.json

JSON

JSON(JavaScript Object Notation) 一种人类可读的文本数据格式。它源于 JavaScript、标准开放、独立于具体编程语言、常用于数据交换。

JSON标准中一共定义了六种值类型

数据类型(Type) 示例(Example)
数组(Array) [值1, 值2, 值3, ...]
布尔值(Boolean) true, false, null
数值(Number) 100, 3.14, -20
对象(Object) {字符串: 值, 字符串: 值, ...}
字符串(String) "一定要用双引号包裹"
// javascript
{
    id: 1,
    name: "Jason",
    human: true,
    hobbies: [
        'eating',
        'playing',
    ],
    features: {
        assets: null,
        weight: 60,
    },
}

通过将其中的所有单引号换为双引号,将对象中的键名改为由双引号包裹的同名字符串,再将对象与数组中最后出现的多余逗号去掉,我们便可以得到与其相对应的JSON数据。

{
    "id": 1,
    "name": "Jason",
    "human": true,
    "hobbies": [
        "eating",
        "playing"
    ],
    "features": {
        "assets": null,
        "weight": 60
    }
}
# python
{
    'id': 1,
    'name': "Jason",
    'human': True,
    'hobbies': [
        'eating',
        'playing',
    ],
    'features': {
        'assets': None,
        'weight': 60,
    },
}

Python字典与JSON格式的主要区别

  1. 字符串键:在JSON中,对象(类似于Python的字典)的键必须是字符串。虽然在Python 3.6+中,字典会保留插入的顺序,但在Python 3.6之前,字典是无序的,而在JSON中,对象的键总是被认为是有序的。
  2. 字符串引号:在JSON中,字符串必须使用双引号(")包裹。在Python中,你可以使用单引号(')、双引号(")或三引号('''或""")。
  3. 数据类型:JSON支持的数据类型包括字符串、数字(整数和浮点数)、布尔值(true/false)、数组(类似于Python的列表)、对象(类似于Python的字典)和null。Python字典的值可以是任何数据类型,包括字符串、数字、列表、字典、元组、集合等。对于布尔值和空值类型,JSON使用true/false/null,而Python的True/False/None。
  4. 注释:Python字典允许在源代码中添加注释,而JSON不支持注释
  5. 尾随逗号:在Python中,字典的键值对可以有一个尾随逗号,而在标准的JSON中,尾随逗号是不允许的(尽管在某些实现中可能被宽容处理)。JSON 5是一个JSON的扩展,它允许尾随逗号。
  6. 复杂对象:Python字典可以包含任何Python对象,包括函数、类实例等。而JSON只能表示简单的数据结构。

JSON 5

JSON 5

JSON 5是一个JSON的扩展,它增加了对一些JavaScript对象字面量特性的支持,例如注释、尾随逗号、单引号字符串等。这使得JSON 5更容易阅读和编写,同时也更易于与JavaScript对象字面量互相转换。

尽管JSON 5提供了更多的灵活性和便利性,但它并不是一个官方的ECMAScript标准,因此在某些环境下可能不被支持。

{
  // json5
  id: 1, // 属性名没有双引号,尾随逗号
  name: "Jason",
  human: true,
  hobbies: [
    'eating',
    'playing', // 数组尾部的尾随逗号
  ],
  features: {
    assets: null, // 对象尾部的尾随逗号
    weight: 60,
  },
}

.yaml

YAML

YAML(Yet Another Markup Language / YAML Ain't Markup Language)不再是一种标记语言,而是一种人类可读的数据序列化语言,它常用于配置文件,可被多种编程语言解析。YAML的定位与JSON相近,但具有自己的特色。

YAML语法细节

字符串(String)
Jason
"Jason"
'Jason'
"Jason"
列表(List)
- Jason
- 18

[Jason, 18,]
[
    "Jason",
    18
]
关联数组(Associative Array)
name: Jason
age: 18

{name: Jason, age: 18,}
{
    "name": "Jason",
    "age": 18
}
缩进结构(Indentation)
Profile:
    name: Jason
    age: 18
    hobbies:
        - eating
        - playing

Profile:
name: Jason
age: 18
hobbies:
    - eating
    - playing

Profile:
  name: Jason
  age: 18
hobbies:
    - - eating
      - playing
{
    "Profile": {
        "name": "Jason",
        "age": 18,
        "hobbies": [
            "eating",
            "playing"
        ]
    }
}

{
    "Profile": null,
    "name": "Jason",
    "age": 18,
    "hobbies": [
        "eating",
        "playing"
    ]
}

{
    "Profile": {
        "name": "Jason",
        "age": 18
    },
    "hobbies": [
        [
            "eating",
            "playing"
        ]
    ]
} 
注释(Comment)
Profile: # 这是一条注释
    name: Jason
    age: 18
{
    "Profile": {
        "name": "Jason",
        "age": 18
    }
}
区块字符(Block Character)
Profile:
    I'm Jason,
    in Shanghai.
{
    "Profile": "I'm Jason, in Shanghai."
}
# 保留换行
Profile: |
    I'm Jason,
    in Shanghai.
{
    "Profile": "I'm Jason,\nin Shanghai.\n"
}
# 折叠换行
Profile: >
    I'm Jason,
    in Shanghai.
{
    "Profile": "I'm Jason, in Shanghai.\n"
}
# 去掉末尾换行符
Profile: |-
    I'm Jason,
    in Shanghai.
{
    "Profile": "I'm Jason,\nin Shanghai."
}
# 保留末尾换行符
Profile: >+
    I'm Jason,
    in Shanghai.
{
    "Profile": "I'm Jason, in Shanghai.\n\n"
}
锚点与引用(Anchor & Reference)
name: Jason
age: name
addr: &addr Shanghai
ship-to: *addr
{
    "name": "Jason",
    "age": "name"
    "addr": "Shanghai",
    "ship-to": "Shanghai"
}
标签(Tag)
name: Jason
age: 18
money: !!str null
# 全局标签
!! str
!<tag:yaml.org,2002:str>

# 本地标签
!str
!<!str>

# 控制标签解析行为
%TAG ! tag:yaml.org,2002:
---
name: Jason
age: 18
money: !str null
{
    "name": "Jason",
    "age": 18,
    "money": "null"
}
文档(Doc)
%TAG ! tag:yaml.org,2002:
--- # 文档开始
name: Jason
age: 18
money: !str null
... # 文档结束
--- # 另一个文档开始
name: Alice
age: 24
money: 1000
... # 另一个文档结束
{
    "name": "Jason",
    "age": 18,
    "money": "null"
}
{
    "name": "Alice",
    "age": 24,
    "money": 1000
}

YAML支持

语言
C/C++ LibYAML
Go go-yaml
Java jvyaml / JYAML
JavaScript eemeli/yaml
Python PyYAML
Objective-C Cocoa-Syck
C# YamlDotNet

YAML 比较 JSON 的优势

更方便的文本内联
html: >-
  <p class="tag">
    Jason
  </p>
{
    "html": "<p class=\"tag\">\n    Jason\n  </p>"
}
更易读的数据关系
addr: &addr Shanghai
ship-to: *addr
{
    "addr": "Shanghai",
    "ship-to": "Shanghai"
}

YAML 比较 JSON 的劣势

复杂语法结构
%TAG !yaml! tag:yaml.org,2002:
---
omap: !yaml!omap
  - name: Jason
  - humal: !!bool true
  - date: 2024-09-10
  - ? money
{
    "omap": [
        {
            "name": "Jason"
        },
        {
            "humal": true
        },
        {
            "date": "2024-09-10T00:00:00.000Z"
        },
        {
            "money": null
        }
    ]
}
容易形成RCE漏洞
!!python/object/apply:subprocess.Popen
- - echo
  - "You have been hacked :)"
# PyYAML
from yaml import load, Loader
# ...
result = load(
    request.from['file'],
    Loader=Loader
)
print(result)

.toml

TOML

TOML(Tom's Obvious, Minimal Language)是Tom Preston-Werner在2013年发明的一种语义明确、配置最小化语言,旨在成为一个小规模、易于使用的语义化配置文件格式,它被设计为可以无二义性地转换为一个哈希表

语法细节

键值对(Key/Value Pair)
name = "Jason"
age = 18
  addr = "Shanghai" # 缩进不影响
profile.age = 18 # 点表示嵌套
profile.addr = "Shanghai"
about.pets.cats = "Pipi"
# profile = "Jason" # 错误,键名不能重复
{
    "name": "Jason",
    "age": 18,
    "addr": "Shanghai",
    "profile": {
        "age": 18,
        "addr": "Shanghai"
    },
    "about": {
        "pets": {
            "cats": "Pipi"
        }
    }
}
字符串(String)
name = "Jason"
name2 = """Jason
Yu
"""
name3 = """
Jason \
Yu\
"""
name4 = 'Jason\nYu'
name5 = '''
Jason
Yu
'''
{
    "name": "Jason",
    "name2": "Jason\nYu\n",
    "name3": "Jason Yu",
    "name4": "Jason\\nYu\n",
    "name5": "Jason\nYu\n"
}
数组(Array)
hobbies = [
    "eating",
    "playing",
]
hobbies2 = [
    [
        "eating",
        "playing",
    ]
]
{
    "hobbies": [
        "eating",
        "playing"
    ],
    "hobbies2": [
        [
            "eating",
            "playing"
        ]
    ]
}
表(Table)
profile.age = 18
profile.addr = "Shanghai"
profile.jobs.EfficLab = true
about.pets.cats = "Pipi"
[profile]
age = 18
addr = "Shanghai"
jobs = { EfficLab = true } # 内联表 只能写在一行
[about.pets]
cats = "Pipi"
# [about.pets] # 错误,表名与键名都不能重复定义
{
    "profile": {
        "age": 18,
        "addr": "Shanghai",
        "jobs": {
            "EfficLab": true
        }
    },
    "about": {
        "pets": {
            "cats": "Pipi"
        }
    }
}
表数组(Array of Tables)
[[profile]]
name = "Jason"
age = 18
[[profile]] # 不同与表 表数组可以重复定义
name = "Alice"
age = 20
profile = [
    { name = "Jason", age = 18 },
    { name = "Alice", age = 20 }
]
{
    "profile": [
        {
            "name": "Jason",
            "age": 18
        },
        {
            "name": "Alice",
            "age": 20
        }
    ]
}
注释(Comment)
# 单行注释
name = "Jason" # 这是一个注释
{
    "name": "Jason"
}

使用限制

顶层表数据无法后置
name = "Jason"
[profile]
age = 18
addr = "Shanghai"
{
    "name": "Jason",
    "profile": {
        "age": 18,
        "addr": "Shanghai"
    }
}
[profile]
age = 18
addr = "Shanghai"
name = "Jason" # 会被归类到 profile 表
{
    "profile": {
        "age": 18,
        "addr": "Shanghai",
        "name": "Jason"
    }
}
内联表无法换行
profile = [
    { name = "Jason", age = 18 },
    { name = "Alice", age = 20 },
]
{
    "profile": [
        {
            "name": "Jason",
            "age": 18
        },
        {
            "name": "Alice",
            "age": 20
        }
    ]
}
可读性依赖于写法
[profile]
age = 18
addr = "Shanghai"
name.surname = "Yu"
[[pets.cat]]
name = "Pipi"
[about]
job = "EfficLab"
[pets.cat.children]
[about.desc]
skill = "CS"
{
    "profile": {
        "age": 18,
        "addr": "Shanghai",
        "name": {
            "surname": "Yu"
        }
    },
    "pets": {
        "cat": [
            {
                "name": "Pipi",
                "children": {}
            }
        ]
    },
    "about": {
        "job": "EfficLab",
        "desc": {
            "skill": "CS"
        }
    }
}