简体   繁体   English

使yaml / ruamel.yaml总是内联转储列表

[英]Making yaml/ruamel.yaml always dump lists inline

How do I make PyYAML or ruamel.yaml always dump lists inline? 如何使PyYAML或ruamel.yaml 总是内联转储列表? Be it lists of YAML elements loaded from an existing file or added from my code. 它是从现有文件加载还是从我的代码添加的YAML元素列表。

When I load YAML from a file and then dump it, it dumps lists inline (see code below). 当我从文件加载YAML然后转储时,它会内联转储列表(请参见下面的代码)。 But if I add a new YAML element with lists to the existing parent object and then dump it, it dumps lists not inline. 但是,如果我将一个带有列表的新YAML元素添加到现有的父对象中,然后转储它,则它不会内联转储列表。

I tried with Python 3.7.3, PyYAML 5.1.1 and ruamel.yaml 0.15.97. 我尝试使用Python 3.7.3,PyYAML 5.1.1和ruamel.yaml 0.15.97。

>>> import ruamel.yaml
>>> ruamel.yaml.__version__
'0.15.97'
>>> raw_yaml = """
... users:
...   user1:
...     comment: comment1
...     keys: ["user1 key1", "user1 key2"]
...     groups: ["user1 group1", "user1 group2"]
... """
>>> yaml = ruamel.yaml.round_trip_load(raw_yaml, preserve_quotes=True)
>>> dump = ruamel.yaml.round_trip_dump(yaml, default_flow_style=None)
>>> print(dump)
users:
  user1:
    comment: comment1
    keys: ["user1 key1", "user1 key2"]
    groups: ["user1 group1", "user1 group2"]
# So far so good, 'keys' and 'groups' are dumped inline
>>> yaml['users']['user2'] = {}
>>> yaml['users']['user2']['comment'] = 'comment2'
>>> yaml['users']['user2']['keys'] = []
>>> yaml['users']['user2']['keys'].append('user2 key1')
>>> yaml['users']['user2']['keys'].append('user2 key2')
>>> yaml['users']['user2']['groups'] = []
>>> yaml['users']['user2']['groups'].append('user2 group1')
>>> yaml['users']['user2']['groups'].append('user2 group2')
>>> dump = ruamel.yaml.round_trip_dump(
...     yaml, default_flow_style=False, default_style="'",
...     indent=2, block_seq_indent=2)
# desired result:
# users:
#   user1:
#     comment: comment1
#     keys: ["user1 key1", "user1 key2"]
#     groups: ["user1 group1", "user1 group2"]
#   user2:
#     comment: comment2
#     keys: ["user2 key1", "user2 key2"]
#     groups: ["user2 group1", "user2 group2"]
>>> print(dump)
'users':
    'user1':
        'comment': 'comment1'
        'keys': ["user1 key1", "user1 key2"]
        'groups': ["user1 group1", "user1 group2"]
    'user2':
        'comment': 'comment2'
        'keys':
            - 'user2 key1'
            - 'user2 key2'
        'groups':
            - 'user2 group1'
            - 'user2 group2'

See above, when I dump just loaded YAML (users['user1']), the lists are inline: 参见上文,当我转储刚加载的YAML(users ['user1'])时,列表是内联的:

keys: ["user1 key1", "user1 key2"]
groups: ["user1 group1", "user1 group2"]

But when I add users['user2'] and then dump entire YAML object, the lists are not inline: 但是,当我添加users ['user2']然后转储整个YAML对象时,列表不是内联的:

'keys':
    - 'user2 key1'
    - 'user2 key2'
'groups':
    - 'user2 group1'
    - 'user2 group2'

If I set 'default_flow_style=True', it dumps the entire element inline: 如果我设置“ default_flow_style = True”,它将内联转储整个元素:

'user2': {'comment': 'comment2', 'keys': ['user2 key1', 'user2 key2'], 'groups': [ 'user2 group1', 'user2 group2']}

This is not what I want. 这不是我想要的。 I want 'comment', 'keys' and 'groups' are dumped on separate lines with lists inline: 我想将“注释”,“键”和“组”转储到带有内联列表的单独行上:

user2:
  comment: comment2
  keys: ["user2 key1", "user2 key2"]
  groups: ["user2 group1", "user2 group2"]

For PyYaml, the situation is practically the same. 对于PyYaml,情况实际上是相同的。

I want lists are being always dumped inline (as for users['user1']). 我希望列表始终以内联方式转储(对于users ['user1'])。 How do I do that? 我怎么做?

I can only answer for PyYAML: The behavior was changed in version 5.1. 我只能回答PyYAML:该行为在5.1版中已更改。

https://github.com/yaml/pyyaml/pull/256 https://github.com/yaml/pyyaml/pull/256

False will output block style always. False将始终输出块样式。 True will output flow style always. True将始终输出流样式。 None will output flow style only for the collections which consists only of scalars. None将仅针对仅包含标量的集合输出流样式。

False is now the default because many users complained that the previous default None wasn't a good one. 现在默认为False ,因为许多用户抱怨以前的默认值None不好。

So you want default_flow_style=None for PyYAML. 因此,您希望default_flow_style=None对于PyYAML。

data = dict( a=dict(aa=dict(aaa = ['x','y']), ab=42) )
print( yaml.dump( data ) )
print( yaml.dump( data, default_flow_style=True ) )
print( yaml.dump( data, default_flow_style=False ) )
print( yaml.dump( data, default_flow_style=None ) )

Output: 输出:

a:
  aa:
    aaa:
    - x
    - y
  ab: 42

{a: {aa: {aaa: [x, y]}, ab: 42}}

a:
  aa:
    aaa:
    - x
    - y
  ab: 42

a:
  aa:
    aaa: [x, y]
  ab: 42```

What you call inline is called flow-style in the YAML documentation. 在YAML文档中,您所谓的内联称为流样式。 There is an option ( default_flow_style ) in both ruamel.yaml to globally have everything flow-style, everything block-style, or have leaf-nodes flow-style (rest block style). ruamel.yaml中都具有一个选项( default_flow_style ),以全局方式具有所有流样式,所有块样式或叶节点流样式(其余块样式)。 This is the old behaviour of PyYAML. 这是PyYAML的旧行为。

However this is not what you want as that affect both sequences and mappings, and you want mappings only. 但是,这不是您想要的,因为它会影响序列和映射,并且只需要映射。

ruamel.yaml , in round-trip-mode, can preserve individual flow-style/block-style as they occur in your file, so you can have eg leave nodes and their parents be flow-style, or all sequences (Python lists) flow-style and all mappings (Python dicts) block-style The latter of course only works as long as mapping is not "under" a sequence, as you cannot have block style within flow-style. ruamel.yaml在往返模式下可以保留文件中出现的单个流样式/块样式,因此您可以使例如离开节点及其父对象成为流样式或所有序列(Python列表)流样式和所有映射(Python dicts)块样式后者当然仅在映射不在序列下时有效,因为您不能在流样式中拥有块样式。

If you start from scratch or with a loaded YAML that has the right format, just make sure any added lists are actually the special internal list subclass that ruamel.yaml uses to preserve comments, style etc and set the flow-style on the added lists 如果您从头开始或使用正确格式的已加载YAML,只需确保任何添加的列表实际上是ruamel.yaml用于保留注释,样式等并在添加的列表上设置流样式的特殊内部列表子类。

import sys
import ruamel.yaml
from ruamel.yaml.scalarstring import DoubleQuotedScalarString as dq

def L(*l):
   ret = ruamel.yaml.comments.CommentedSeq(l)
   ret.fa.set_flow_style()
   return ret   


raw_yaml = """\
users:
   user1:
    comment: comment1
    keys: ["user1 key1", "user1 key2"]
    groups: ["user1 group1", "user1 group2"]
"""

yaml = ruamel.yaml.YAML()
# yaml.indent(mapping=4, sequence=4, offset=2)
yaml.preserve_quotes = True
data = yaml.load(raw_yaml)
data['users']['user2'] = {}
data['users']['user2']['comment'] = 'comment2'
data['users']['user2']['keys'] = L()
data['users']['user2']['keys'].append('user2 key1')
data['users']['user2']['keys'].append('user2 key2')
data['users']['user2']['groups'] = L('abc', L('user2 group1', dq('user2 group2')))
# print(data)
yaml.dump(data, sys.stdout)

which gives: 这使:

users:
  user1:
    comment: comment1
    keys: ["user1 key1", "user1 key2"]
    groups: ["user1 group1", "user1 group2"]
  user2:
    comment: comment2
    keys: [user2 key1, user2 key2]
    groups: [abc, [user2 group1, "user2 group2"]]

Since you want every list to be represented as a flow style sequence, it is also possible to change the representer for all lists, by subclassing the Representer, but the above gives you finer control and allows you to flow exactly those lists you want in flow-style. 由于您希望将每个列表表示为流样式序列,因此也可以通过将Representer子类化来更改所有列表的表示符,但是上面的内容为您提供了更好的控制,并允许您准确地在流式列表中流动这些列表。 -样式。


ruamel.yaml (and PyYAML) use a streaming interface, doing print(dump(data)) instead of dump(data, sys.stdout) makes the dump be done to a buffer in memory and then you stream that buffer. ruamel.yaml (和PyYAML)使用流接口,执行print(dump(data))而不是dump(data, sys.stdout)使转储完成到内存中的缓冲区,然后对该缓冲区进行流式处理。 It is inefficient in time and space, don't do it . 时间和空间效率很低, 请不要这样做

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM