ruamel.yaml 类似日期时间值的奇怪行为

ruamel.yaml weird behavior with datetime like values

check.py

import os
from pathlib import Path
import sys
import json

from ruamel.yaml import YAML
yaml = YAML(pure=True)
yaml.sort_base_mapping_type_on_output = None
yaml.indent(mapping=2, sequence=4, offset=2)


def yamlCheck(file: os.PathLike):
    with open(file, 'r', encoding='utf8') as yr:
        data = yaml.load(yr)

    print(data)

    check = json.dumps(data, indent=4, ensure_ascii=False)
    print(check)


if __name__ == "__main__":
    yamlCheck('test.yaml')

test1.yaml,有错误。

a:
  - b: 2
  - 123
  - 2000-12-26

test2.yaml,运行正常

a:
  - b: 2
  - 123
  - 111-12-26

test2.yaml,运行正常

a:
  - b: 2
  - 123
  - 2000-12-26111

runs ok表示代码运行无误,json.dumps加载好OrderedDict数据。

使用 print(data) 的输出我认为 ruamel.yaml 已经将 xxxx-xx-xx 格式的数据解析为 datetime 并用它构造一个 datetime 对象,这导致 json.dumps 函数的错误。

那么ruamel.yaml决定将值解析为日期时间的标准是什么?

我不确定你认为奇怪的行为是什么,ruamel.yaml 使用常规 表达式将标量隐式标记为 resolver.py 中定义的时间戳。 该正则表达式改编自中的正则表达式 Timestamp Language Independent Type for YAML.

正则表达式假定年份为 4 位数字,月份和日期最多为两位。有效 datetime.datedatetime.datetime(例如,没有月份 2000-12-44 匹配正则表达式,但不是有效日期) 只在合成阶段很晚才检查,当它失败时,标量被假定为一个字符串。

所以 2000-12-26 作为 date 加载是正常的, 111-12-262000-12-26111 都加载 作为字符串。

由于 json.dumps() 不支持转储 datetime.date 个实例,您将拥有 通过指定 default=str 将类型显式转换为字符串,以防止出现错误。

在我看来,奇怪的是你假设 json.dumps() 加载了一些东西 (“json.dumps 加载 OrderedDict 数据”)。 顾名思义,该例程不会加载,它会转储,并且它可以 dump OrderedDict 是因为它是 dict

的子类

这也是低效的 print() 改为 json.dumps() 的结果 直接转储到 sys.stdout.

import sys
import json
import ruamel.yaml

yaml_strs = [
"""\
a:
  - b: 2
  - 123
  - 2000-12-26
""",
"""\
a:
  - b: 2
  - 123
  - 111-12-26
""",
"""\
a:
  - b: 2
  - 123
  - 2000-12-26111
"""
]

yaml = ruamel.yaml.YAML()
for yaml_str in yaml_strs:
    data = yaml.load(yaml_str)
    print(type(data['a'][2]))
    try:
        json.dump(data, sys.stdout, default=str)
        print() # since json.dump doesn't write a final newline
    except TypeError as e:
        print(e)

给出:

<class 'datetime.date'>
{"a": [{"b": 2}, 123, "2000-12-26"]}
<class 'str'>
{"a": [{"b": 2}, 123, "111-12-26"]}
<class 'str'>
{"a": [{"b": 2}, 123, "2000-12-26111"]}