将带有标签的 YAML 转储为 JSON

dumping YAML with tags as JSON

我知道我可以使用 ruamel.yaml 加载带有标签的文件。但是当我想在没有它们的情况下转储时,我得到了一个错误。简化示例:-

from ruamel.yaml import YAML
from json import dumps
import sys

yaml = YAML()
data = yaml.load(
"""
!mytag
a: 1
b: 2
c: 2022-05-01
"""
)

try:
    yaml2 = YAML(typ='safe', pure=True)
    yaml.default_flow_style = True
    yaml2.dump(data, sys.stdout)
except Exception as e:
    print('exception dumping using yaml', e)
try:
    print(dumps(data))
except Exception as e:
    print('exception dumping using json', e)

exception dumping using cannot represent an object: ordereddict([('a', 1), ('b', 2), ('c', datetime.date(2022, 5, 1))])

exception dumping using json Object of type date is not JSON serializable

我无法更改 load() 而不会在标签上出现错误。如何获得剥离标签的输出(YAML 或 JSON)?

你得到这个错误是因为既不是安全的转储程序(无论是否纯正),也不是 JSON,不了解 ruamel.yaml 内部 保留注释、标记、block/flow-style 等的类型

转储为 YAML,您可以使用备用转储方法注册这些类型。由于 JSON 这更复杂 作为 AFAIK,你只能转换 leaf-nodes (即 YAML 标量,你会是 能够使用它来转储作为键值 c).

加载的 datetime.datetime 实例

我将 YAML 用作可读、可编辑和以编程方式更新的配置文件 如果文件不早于相应的 YAML(如果 它较旧 JSON 是从 YAML 创建的)。为了 dump(s) 要做的事情是 递归生成 Python 原语 JSON 理解。

下面是这样做的,但是除了日期时间之外还有其他构造 JSON 不允许的实例。例如。使用序列或字典时 作为键(在 YAML 中允许,但在 JSON 中不允许)。对于那些是 sequences 我连接元素的字符串表示 :

from ruamel.yaml import YAML
import sys
import datetime
import json
from collections.abc import Mapping

yaml = YAML()
data = yaml.load("""\
!mytag
a: 1
b: 2
c: 2022-05-01
[d, e]: !myseq [42, 196]
{f: g, 18: y}: !myscalar x
""")

def json_dump(data, out, indent=None):
    def scalar(obj):
        if obj is None:
            return None
        if isinstance(obj, (datetime.date, datetime.datetime)):
            return str(obj)
        if isinstance(obj, ruamel.yaml.scalarbool.ScalarBoolean):
            return obj == 1
        if isinstance(obj, bool):
            return bool(obj)
        if isinstance(obj, int):
            return int(obj)
        if isinstance(obj, float):
            return float(obj)
        if isinstance(obj, tuple):
            return '_'.join([str(x) for x in obj])
        if isinstance(obj, Mapping):
            return '_'.join([f'{k}-{v}' for k, v in obj.items()])
        if not isinstance(obj, str): print('type', type(obj))
        return obj

    def prep(obj):
        if isinstance(obj, dict):
            return {scalar(k): prep(v) for k, v in obj.items()}
        if isinstance(obj, list):
            return [prep(elem) for elem in obj]
        if isinstance(obj, ruamel.yaml.comments.TaggedScalar):
            return prep(obj.value)
        return scalar(obj)

    res = prep(data)
    json.dump(res, out, indent=indent)


json_dump(data, sys.stdout, indent=2)

给出:

{
  "a": 1,
  "b": 2,
  "c": "2022-05-01",
  "d_e": [
    42,
    196
  ],
  "f-g_18-y": "x"
}