目录
书写格式 YAML.rb is YAML for Ruby | Cookbook
安装
Python包 pyyaml: https://pyyaml.org/wiki/PyYAMLDocumentation
# 安装
pip install pyyaml
书写格式 YAML.rb is YAML for Ruby | Cookbook
文件名后缀:.yml
- 大小写敏感
- 使用缩进表示层级关系
- 缩进不允许使用tab,只允许空格
- 缩进的空格数不重要,只要相同层级的元素左对齐即可
- '#' 表示注释
- YAML 流是零个或多个文档的集合。空流不包含任何文档。多个文档以 --- 分隔。
- 时间必须使用ISO 8601格式,时间和日期之间使用T连接,最后使用+代表时区:2018-02-17T15:02:31+08:00
- 数据引用: & + 数据名 定义锚点;* + 锚点名 引用锚点; <<: *锚点 合并到当前数据
隐式文档示例:
- Multimedia
- Internet
- Education
显式文档示例:
---
- Afterstep
- CTWM
- Oroborus
...
同一流中多个文档的示例:
---
- Ada
- APL
- ASP
- Assembly
- Awk
---
- Basic
---
- C
- C# # Note that comments are denoted with ' #' (space then #).
- C++
- Cold Fusion
块序列
在块上下文中,列表序列条目由 -
(破折号然后空格)表示:
# YAML
- The Dagger 'Narthanc'
- The Dagger 'Nimthanc'
- The Dagger 'Dethanc'
# Python
["The Dagger 'Narthanc'", "The Dagger 'Nimthanc'", "The Dagger 'Dethanc'"]
块序列可以嵌套:
# YAML
-
- HTML
- LaTeX
- SGML
- VRML
- XML
- YAML
-
- BSD
- GNU Hurd
- Linux
# Python
[['HTML', 'LaTeX', 'SGML', 'VRML', 'XML', 'YAML'], ['BSD', 'GNU Hurd', 'Linux']]
没有必要用新行开始嵌套序列:
# YAML
- 1.1
- - 2.1
- 2.2
- - - 3.1
- 3.2
- 3.3
# Python
[1.1, [2.1, 2.2], [[3.1, 3.2, 3.3]]]
块序列可以嵌套到块映射。请注意,在这种情况下,不需要缩进序列。
# YAML
left hand:
- Ring of Teleportation
- Ring of Speed
right hand:
- Ring of Resist Fire
- Ring of Resist Cold
- Ring of Resist Poison
# Python
{'right hand': ['Ring of Resist Fire', 'Ring of Resist Cold', 'Ring of Resist Poison'],
'left hand': ['Ring of Teleportation', 'Ring of Speed']}
块映射
在块上下文中,映射的键和值由:
(冒号然后空格)分隔:
# YAML
base armor class: 0
base damage: [4,4]
plus to-hit: 12
plus to-dam: 16
plus to-ac: 0
# Python
{'plus to-hit': 12, 'base damage': [4, 4], 'base armor class': 0, 'plus to-ac': 0, 'plus to-dam': 16}
复杂的键用?
(问号然后空格)表示:
# YAML
? !!python/tuple [0,0]
: The Hero
? !!python/tuple [0,1]
: Treasure
? !!python/tuple [1,0]
: Treasure
? !!python/tuple [1,1]
: The Dragon
# Python
{(0, 1): 'Treasure', (1, 0): 'Treasure', (0, 0): 'The Hero', (1, 1): 'The Dragon'}
块映射可以嵌套:
# YAML
hero:
hp: 34
sp: 8
level: 4
orc:
hp: 12
sp: 0
level: 2
# Python
{'hero': {'hp': 34, 'sp': 8, 'level': 4}, 'orc': {'hp': 12, 'sp': 0, 'level': 2}}
块映射可以嵌套在块序列中:
# YAML
- name: PyYAML
status: 4
license: MIT
language: Python
- name: PySyck
status: 5
license: BSD
language: Python
# Python
[{'status': 4, 'language': 'Python', 'name': 'PyYAML', 'license': 'MIT'},
{'status': 5, 'license': 'BSD', 'name': 'PySyck', 'language': 'Python'}]
流集合
YAML 中流集合的语法与 Python 中的列表和字典构造函数的语法非常接近:
# YAML
{ str: [15, 17], con: [16, 16], dex: [17, 18], wis: [16, 16], int: [10, 13], chr: [5, 8] }
# Python
{'dex': [17, 18], 'int': [10, 13], 'chr': [5, 8], 'wis': [16, 16], 'str': [15, 17], 'con': [16, 16]}
标量
YAML 中有 5 种类型的标量:普通、单引号、双引号、文字和折叠:
# YAML
plain: Scroll of Remove Curse
single-quoted: 'EASY_KNOW'
double-quoted: "?"
literal: | # Borrowed from http://www.kersbergen.com/flump/religion.html
by hjw ___
__ /.-.\
/ )_____________\\ Y
/_ /=== == === === =\ _\_
( /)=== == === === == Y \
`-------------------( o )
\___/
folded: >
It removes all ordinary curses from all equipped items.
Heavy or permanent curses are unaffected.
# Python
{'plain': 'Scroll of Remove Curse',
'literal':
'by hjw ___\n'
' __ /.-.\\\n'
' / )_____________\\\\ Y\n'
' /_ /=== == === === =\\ _\\_\n'
'( /)=== == === === == Y \\\n'
' `-------------------( o )\n'
' \\___/\n',
'single-quoted': 'EASY_KNOW',
'double-quoted': '?',
'folded': 'It removes all ordinary curses from all equipped items. Heavy or permanent curses are unaffected.\n'}
每种风格都有自己的怪癖。普通标量不使用指示符来表示其开始和结束,因此它是最受限制的样式。它的自然应用是属性和参数的名称。
使用单引号标量,您可以表示任何不包含特殊字符的值。除了一对相邻的引号''
被一个单独的单引号替换外,单引号标量不会发生转义'
。
双引号是最强大的样式,也是唯一可以表达任何标量值的样式。双引号标量允许转义. 使用转义序列\x*
和\u***
,您可以表示任何 ASCII 或 Unicode 字符。
有两种块标量样式:literal和folded。文字样式最适合源代码等大块文本。折叠样式与字面样式类似,但两个相邻的非空行连接到由空格字符分隔的单行。
YAML 标签和 Python 类型
下表描述了如何将具有不同标签的节点转换为 Python 对象。
YAML 标签 | 蟒蛇型 |
---|---|
标准 YAML 标签 | |
!!null | None |
!!bool | bool |
!!int | int 或long (int 在 Python 3 中) |
!!float | float |
!!binary | str (bytes 在 Python 3 中) |
!!timestamp | datetime.datetime |
!!omap , !!pairs | list 对 |
!!set | set |
!!str | str 或unicode (str 在 Python 3 中) |
!!seq | list |
!!map | dict |
特定于 Python 的标签 | |
!!python/none | None |
!!python/bool | bool |
!!python/bytes | (bytes 在 Python 3 中) |
!!python/str | str (str 在 Python 3 中) |
!!python/unicode | unicode (str 在 Python 3 中) |
!!python/int | int |
!!python/long | long (int 在 Python 3 中) |
!!python/float | float |
!!python/complex | complex |
!!python/list | list |
!!python/tuple | tuple |
!!python/dict | dict |
复杂的 Python 标签 | |
!!python/name:module.name | module.name |
!!python/module:package.module | package.module |
!!python/object:module.cls | module.cls 实例 |
!!python/object/new:module.cls | module.cls 实例 |
!!python/object/apply:module.f | 的价值 f(...) |
# yaml数据为键值对格式, :后面必须留有一个空格
name: zhangsan
age: 18
# 根据缩进来判断层级目录
class:
class_name: one
class_no: 1
# 列表:- 后面必须留有一个空格 后面再跟数据
end:
- id
- index
- list_id
- list_index
# 注释,---为每条数据分隔符
---
name: 李四
age: 19
class:
class_name: two
class_no: 2
end:
- id1
- index1
- list_id1
- list_index1
# 循环遍历读取结果
{'name': 'zhangsan', 'age': 18, 'class': {'class_name': 'one', 'class_no': 1}, 'end': ['id', 'index', 'list_id', 'list_index']}
{'name': '李四', 'age': 19, 'class': {'class_name': 'two', 'class_no': 2}, 'end': ['id1', 'index1', 'list_id1', 'list_index1']}
- age: 18
class:
class_name: one
class_no: 1
end:
- id
- index
- list_id
- list_index
name: zhangsan
- age: 19
class:
class_name: two
class_no: 2
end:
- id1
- index1
- list_id1
- list_index1
name: "\u674E\u56DB"
# 读取结果
[{'age': 18, 'class': {'class_name': 'one', 'class_no': 1}, 'end': ['id', 'index', 'list_id', 'list_index'], 'name': 'zhangsan'}, {'age': 19, 'class': {'class_name': 'two', 'class_no': 2}, 'end': ['id1', 'index1', 'list_id1', 'list_index1'], 'name': '李四'}]
读取
读取字符串类型的yaml数据
yaml.load() 函数将YAML 文档转换为 Python 对象
import yaml
data = """
name: zhangsan
age: 17
class:
- class_one
- class_good
- class_nice
"""
yaml.load(data)
读取yaml格式文件
with open(yaml_path, "r", encoding="utf-8") as f:
data = yaml.load_all(stream=f.read(), Loader=yaml.FullLoader)
f.close()
写入
import yaml
yaml.dump(data)
# 写入到文件
data_python = [
{'name': 'zhangsan', 'age': 18, 'class': {'class_name': 'one', 'class_no': 1},
'end': ['id', 'index', 'list_id', 'list_index']},
{'name': '李四', 'age': 19, 'class': {'class_name': 'two', 'class_no': 2},
'end': ['id1', 'index1', 'list_id1', 'list_index1']}
]
with open(new_yaml_file, "w", encoding="utf-8") as f:
yaml.dump(data=data_python, stream=f)
f.close()
# 写入结果
- age: 18
class:
class_name: one
class_no: 1
end:
- id
- index
- list_id
- list_index
name: zhangsan
- age: 19
class:
class_name: two
class_no: 2
end:
- id1
- index1
- list_id1
- list_index1
name: "\u674E\u56DB"
# yaml.dump_all()
print(yaml.dump_all([1, 2, 3, {"name": "lisi"}]))
# 输出结果
1
--- 2
--- 3
---
name: lisi
yaml 文件内引用其他 yaml 文件的内容
临时可变参数文件 b.yml
# b.yml 存储可变参数
headers:
Authorization: "aaaaaa_bbbbbbb"
Host: "www.baidu.com"
body:
appVersion: "1.0"
logtime: 0
userid: "100000"
ver: "1.0"
vercode: 1
channel: "baidu"
# a.yml 存储全局信息,调用b.yml 中的内容
headers:
User-Agent: "OkHttp Headers.java"
Accept: "*/*"
Accept-Encoding: ""
encrypttype: "1"
Content-Type: "application/json; charset=utf-8"
Content-Length: "666"
Connection: "Keep-Alive"
Authorization: !auth b.yml
Host: !host b.yml
body_json:
online: "0"
imei: ""
mac: "A1:2C:3D:A4:A4:A0"
platform: "1"
net: "wifi"
phone: "MI 9|android10"
screen: "1080*2135"
deviceid: "11111111111111111111111"
androidid: "20202222222"
appType: ""
from: "0"
oaid: "000000"
ip: ""
android_version: 29
ua: "MI 9"
os: "android10"
osid: "QKQ1.190825.002"
appVersion: !appVersion b.yml
# 添加 yaml 构造器 自定义引用方法
import yaml
import time
import os
import jsonpath
import json
def yaml_full_file(loader, node):
""" 构造器函数。用来读取某个yaml文件内的全部内容 """
# print(loader.name, node.value, node.tag)
file_name = os.path.join(os.path.dirname(loader.name), node.value) # 引用文件名
with open(file_name, encoding="utf-8") as f:
return yaml.load(f, Loader=yaml.FullLoader)
def yaml_value_by_appversion(loader, node, key="appVersion"):
""" 构造器函数。用来读取某个yaml文件内指定 key 的值 """
file_name = os.path.join(os.path.dirname(loader.name), node.value) # 引用文件名
with open(file_name, encoding="utf-8") as f:
data = yaml.load(f, Loader=yaml.FullLoader)
return jsonpath.jsonpath(data, f"$..{key}")[0]
def yaml_value_by_auth(loader, node, key="Authorization"):
""" 构造器函数。用来读取某个yaml文件内指定 key 的值 """
file_name = os.path.join(os.path.dirname(loader.name), node.value) # 引用文件名
with open(file_name, encoding="utf-8") as f:
data = yaml.load(f, Loader=yaml.FullLoader)
return jsonpath.jsonpath(data, f"$..{key}")[0]
def yaml_value_by_host(loader, node, key="Host"):
""" 构造器函数。用来读取某个yaml文件内指定 key 的值 """
file_name = os.path.join(os.path.dirname(loader.name), node.value) # 引用文件名
with open(file_name, encoding="utf-8") as f:
data = yaml.load(f, Loader=yaml.FullLoader)
return jsonpath.jsonpath(data, f"$..{key}")[0]
# 添加自定义构造器
yaml.add_constructor("!include", yaml_full_file)
yaml.add_constructor("!appVersion", yaml_value_by_appversion)
yaml.add_constructor("!auth", yaml_value_by_auth)
yaml.add_constructor("!host", yaml_value_by_host)
# 读取数据
datas = yaml.load(stream=open("./a.yml", mode="r", encoding="utf-8"), Loader=yaml.FullLoader)
# print(datas['headers'])
print(datas.get("headers"))
print(datas.get("body_json"))
读取yaml文件内的自定义函数执行结果
"""
读取yaml文件内有引用方法的数据,需要在另一个文件内创建需要的函数
"""
from . import custom_methods
import yaml
import os
def read_yaml(file):
with open(file, "r", encoding="utf-8") as f:
return yaml.load(f, Loader=yaml.FullLoader)
def get_files_name_from_path(path, suffix=".py"):
"""从指定目录下查找指定格式的文件列表"""
for root, dirs, files in os.walk(path):
# 循环目录返回文件名
if len(dirs) > 0:
for dir in dirs:
# 递归处理目录,将结果内的文件名列表追加到列表内
files_son = get_files_name_from_path(os.path.join(root, dir), suffix=suffix)
files.extend(files_son)
# 复制列表,一个列表用来循环判断,一个用来删除 (****列表删除的坑****)
files_copy = files.copy()
for file in files:
if os.path.splitext(file)[-1] != suffix:
files_copy.remove(file)
else:
files_copy[files_copy.index(file)] = os.path.join(root, file)
# 返回全部文件名
return files_copy
def auto_import_function(function_name, files):
"""从指定目录文件列表内查找指定方法,自动导包并返回方法名"""
for file in files:
path, filename = os.path.split(file)
filename = os.path.splitext(filename)[0]
if hasattr(filename, function_name):
# exec() 方法 执行导包代码
exec(f"from {path} import {filename}")
return filename
return
def str_dispose(strings):
""" 判断一个字符串是否符合自定义方法引用规范 """
return True if str(strings).startswith("${") and str(strings).endswith("}") else False
def variable_substitution(strings, moudule_name=custom_methods): # 这里要写入默认值:自定义应用方法文件名
"""
从指定py文件内查找自定义引用格式的函数方法名执行结果
自定义方法规范:必须在指定的 moudule_name 文件内
:param strings: 带有自定义引用的yaml字符串
:param moudule_name: 包含自定义引用方法的文件名(不包含后缀)
:return: 如果有则返回方法执行后的数据
"""
name = str(strings).replace("${", "").replace("}", "")
# 判断在指定文件内是否有定义指定函数名的方法
if hasattr(moudule_name, name):
# 返回该方法执行后的数据
return getattr(moudule_name, name)()
else:
return
def analyze_data(data):
""" 递归处理数据 """
# 检查数据格式,是否是字典类型
if isinstance(data, dict):
for key, value in data.items():
# 检查字典数据里的value的数据类型是否是可迭代
if isinstance(value, (dict, list, tuple)):
# 可迭代数据递归处理
analyze_data(value)
else: # 不可迭代数据,检查是否是符合自定义函数方法引用格式
if str_dispose(value):
data[key] = variable_substitution(value)
else: # 不处理未使用自定义函数方法引用格式的数据
pass
# 检查数据格式是否可迭代
elif isinstance(data, (list, tuple)):
# 将可迭代数据内每一项数据进行递归处理
for index in range(len(data)):
data[index] = analyze_data(data[index])
else: # 这里处理其他类型数据
if str_dispose(data): # 检查是否是符合自定义函数方法引用格式
return variable_substitution(data)
else: # 直接返回未使用自定义函数方法引用格式的数据
return data
return data