我希望能够在PyYAML的dump()函数生成的YAML中生成锚点。有没有办法做到这一点?理想情况下,锚点的名称与YAML节点的名称相同。
示例:
import yaml
yaml.dump({'a': [1,2,3]})
'a: [1, 2, 3]\n'
我希望能够做的就像生成YAML一样:
import yaml
yaml.dump({'a': [1,2,3]})
'a: &a [1, 2, 3]\n'
我可以编写自定义发射器或转储器来执行此操作吗?还有另一种方式吗?
答案 0 :(得分:1)
默认情况下,只有在检测到对先前看到的对象的引用时才会发出锚点:
>>> import yaml
>>>
>>> foo = {'a': [1,2,3]}
>>> doc = (foo,foo)
>>>
>>> print yaml.safe_dump(doc, default_flow_style=False)
- &id001
a:
- 1
- 2
- 3
- *id001
如果要覆盖其命名方式,您必须自定义Dumper class,特别是generate_anchor()
功能。 ANCHOR_TEMPLATE
也可能有用。
在您的示例中,节点名称很简单,但您需要考虑YAML值的许多可能性,即它可以是序列而不是单个值:
>>> import yaml
>>>
>>> foo = {('a', 'b', 'c'): [1,2,3]}
>>> doc = (foo,foo)
>>>
>>> print yaml.dump(doc, default_flow_style=False)
!!python/tuple
- &id001
? !!python/tuple
- a
- b
- c
: - 1
- 2
- 3
- *id001
答案 1 :(得分:1)
这并不容易。除非您要用于锚点的数据是 in 节点。这是因为锚点附加到节点内容,在您的示例中[1,2,3]'并且不知道这个值与关键字' a'相关联。
l = [1, 2, 3]
foo = {'a': l, 'b': l}
class SpecialAnchor(yaml.Dumper):
def generate_anchor(self, node):
print('Generating anchor for {}'.format(str(node)))
anchor = super().generate_anchor(node)
print('Generated "{}"'.format(anchor))
return anchor
y1 = yaml.dump(foo, Dumper=Anchor)
给你:
Generating anchor for SequenceNode(tag='tag:yaml.org,2002:seq', value=[ScalarNode(tag='tag:yaml.org,2002:int', value='1'), ScalarNode(tag='tag:yaml.org,2002:int', value='2'), ScalarNode(tag='tag:yaml.org,2002:int', value='3')])
Generated "id001"
a: &id001 [1, 2, 3]
b: *id001
到目前为止,我还没有找到办法获得钥匙' a'给定节点......
答案 2 :(得分:1)
我写了一个自定义锚类来强制顶级节点的锚值。它不会简单地覆盖锚点(使用generate_anchor),但实际上会强制发出Anchor,即使稍后没有引用该节点:
class CustomAnchor(yaml.Dumper):
def __init__(self,*args,**kwargs):
super(CustomAnchor,self).__init__(*args,**kwargs)
self.depth = 0
self.basekey = None
self.newanchors = {}
def anchor_node(self, node):
self.depth += 1
if self.depth == 2:
assert isinstance(node,yaml.ScalarNode), "yaml node not a string: %s"%node
self.basekey = str(node.value)
node.value = self.basekey+"_ALIAS"
if self.depth == 3:
assert self.basekey, "could not find base key for value: %s"%node
self.newanchors[node] = self.basekey
super(CustomAnchor,self).anchor_node(node)
if self.newanchors:
self.anchors.update(self.newanchors)
self.newanchors.clear()
请注意,我覆盖节点名称后缀为“_ALIAS”,但您可以删除该行以保留节点名称和锚名称相同,或将其更改为其他名称。
E.g。倾销{'FOO':'BAR'}导致:
FOO_ALIAS:& FOO BAR
另外,我一次只编写它来处理单个顶级键/值对,它只会强制使用顶级键的锚点。如果要将dict转换为YAML文件,其中所有键都是顶级YAML节点,则需要迭代dict并将每个键/值对转储为{key:value},或者重写此类以处理dict有多个键。
答案 3 :(得分:0)
我根本无法获得@beeb的答案,所以我继续尝试推广@ aaa90210的答案
import yaml
class _CustomAnchor(yaml.Dumper):
anchor_tags = {}
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.new_anchors = {}
self.anchor_next = None
def anchor_node(self, node):
if self.anchor_next is not None:
self.new_anchors[node] = self.anchor_next
self.anchor_next = None
if isinstance(node.value, str) and node.value in self.anchor_tags:
self.anchor_next = self.anchor_tags[node.value]
super().anchor_node(node)
if self.new_anchors:
self.anchors.update(self.new_anchors)
self.new_anchors.clear()
def CustomAnchor(tags):
return type('CustomAnchor', (_CustomAnchor,), {'anchor_tags': tags})
print(yaml.dump(foo, Dumper=CustomAnchor({'a': 'a_name'})))
这没有提供一种方法来区分两个具有相同名称值的节点,这将需要与XML的xpath等效的yaml,我在pyyaml中看不到:(
通过类工厂CustomAnchor
,您可以基于节点值传入锚字典。 {value: anchor_name}