【问题标题】:Is it possible to have aliases in a multi-document YAML stream that span all documents?是否可以在跨所有文档的多文档 YAML 流中使用别名?
【发布时间】:2017-04-03 18:35:21
【问题描述】:

这就是我想要做的(代码在 Python 3 中):

import ruamel.yaml as yaml
from print import pprint

yaml_document_with_aliases = """
title: test
choices: &C
  a: one
  b: two
  c: three
---
title: test 2
choices: *C
"""

items = list(yaml.load_all(yaml_document_with_aliases))

结果是:

ComposerError: found undefined alias 'C'

当我使用非基于文档的 YAML 文件时,这将按预期工作:

import ruamel.yaml as yaml
from print import pprint

yaml_nodes_with_aliases = """
-
  title: test
  choices: &C
    a: one
    b: two
    c: three
-
  title: test 2
  choices: *C
"""

items = yaml.load(yaml_nodes_with_aliases)

pprint(items)

结果:

[{'choices': {'a': 'one', 'b': 'two', 'c': 'three'}, 'title': 'test'},
 {'choices': {'a': 'one', 'b': 'two', 'c': 'three'}, 'title': 'test 2'}]

(无论如何我都想完成)


由于现在不可能,我正在使用以下脆弱的解决方法:

def yaml_load_all_with_aliases(yaml_text):
    if not yaml_text.startswith('---'):
        yaml_text = '---\n' + yaml_text
    for pat, repl in [('^', '  '), ('^\s*---\s*$', '-'), ('^\s+\.{3}$\n', '')]:
        yaml_text = re.sub(pat, repl, yaml_text, flags=re.MULTILINE)
    yaml_text = yaml_text.strip()
    return yaml.safe_load(yaml_text)

【问题讨论】:

  • 您应该使用safe_load_all()safe_load(),除非您在真实文件中标记了 YAML 内容。这并不能解决找不到锚点的问题,但可以防止潜在的恶意 YAML 在您的程序中执行任意代码

标签: python yaml ruamel.yaml


【解决方案1】:

这里的问题是:

title: test
choices: &C
  a: one
  b: two
  c: three
---
title: test 2
choices: *C

不是一个文档,这是一个文件中的两个 YAML 文档。锚定义 &C 不会从一个 YAML 文档传递到另一个 YAML 文档,它只能在文档分隔符 --- 之前使用。

如果您愿意在单个 YAML 流中将所有锚点“转移”到以下文档,您可以在 Composer 类上移植一个新的 compose_document 方法(即对其进行猴子补丁):

import sys
import ruamel.yaml

yaml_str = """\
title: test
choices: &C
  a: one
  b: two
  c: three
---
title: test 2
choices: *C
"""


def my_compose_document(self):
    self.get_event()
    node = self.compose_node(None, None)
    self.get_event()
    # this prevents cleaning of anchors between documents in **one stream**
    # self.anchors = {}
    return node

ruamel.yaml.composer.Composer.compose_document = my_compose_document

datas = []
for data in ruamel.yaml.safe_load_all(yaml_str):
    datas.append(data)

datas[0]['choices']['a'] = 1
for data in datas:
    ruamel.yaml.round_trip_dump(data, sys.stdout, explicit_start=True)

给出:

---
title: test
choices:
  a: 1
  b: two
  c: three
---
title: test 2
choices:
  a: one
  b: two
  c: three

请注意,这将为您提供带有键 abc 的字典的副本

(如果密钥排序和 cmets 的保存很重要,请使用 round_trip_load_all 而不是 safe_load_all

【讨论】:

  • 首先,感谢您这么快回答:) 原谅我,我以为我的术语是对的……那你怎么称呼它?多文档?而且(最重要的是)没有办法将别名定义从一个文档传递到下一个文档吗?
  • @gschizas YAML 1.2 讨论了文档和流,后者可以有多个(或一个或零个)文档。 (safe_)load_all 用于迭代文档。在普通加载器中,锚点和别名信息被解析为and discarded,因为 YAML 被组合到本机数据结构中。通过往返加载,您可能可以将其从一个记录的文件移植到另一个文件,因为在这种情况下,锚信息不会被丢弃(我正在调查,这不是微不足道的)
  • 谢谢 - 我认为这是我缺少的东西,或者需要更多开发而不是我自己可以处理的东西。目前,我使用了一种愚蠢而脆弱的解决方法。
  • @gschizas 在尝试了一种迂回的方式后,我发现了一些可能是一个更简单的解决方案,除非你的别名字典不应该只是具有相同的值,而是实际上在内存中是相同的字典(即,如果你改变加载后一个上的值,您更改另一个)。
  • 我不介意。事实上,它甚至可能更可取。我只是用这个字典来构造别的东西,加载后我把它当作只读的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-13
  • 1970-01-01
  • 2019-06-21
  • 2010-09-13
  • 1970-01-01
  • 2020-04-09
相关资源
最近更新 更多