如何读取 YAML 文件中的组件,以便我可以使用 ruamel.yaml 编辑它的键值?
问题描述
这是我的 YAML 文件(input.yaml
):
This is my YAML file (input.yaml
):
team_member:
name: Max
hobbies:
- Reading
team_leader:
name: Stuart
hobbies:
- dancing
我想编辑此 YAML 文件以在关键爱好"中添加更多值,例如:
I want to edit this YAML file to add more values in key 'hobbies', example:
team_member:
name: Max
hobbies:
- Reading
- Painting
team_leader:
name: Stuart
hobbies:
- Dancing
- Fishing
我尝试实现代码 Anthon 以适应我的情况,但它根本没有帮助,因为缩进级别该 YAML 文件与我的不同.
示例:
I tried to implement the code Anthon to fit my situation but it didn't helped at all, because the indention level of that YAML file is different from mine.
Example:
import sys
import ruamel.yaml
yaml = ruamel.yaml.YAML()
# yaml.preserve_quotes = True
with open('input.yaml') as fp:
data = yaml.load(fp)
for elem in data:
if elem['name'] == 'Stuart':
elem['hobbies'] = ['Fishing']
break # no need to iterate further
yaml.dump(data, sys.stdout)
我收到错误TypeError('string indices must be integers',)",我知道这段代码可能完全错误,但我是 ruamel.yaml 的新手.
I get error "TypeError('string indices must be integers',)", I know this code might be completely wrong, but I am new to ruamel.yaml.
如何编码?
解决方案
显示的错误消息中缺少的是行号(我假设它是 9).那指向那条线
The thing missing form the error message displayed is the line number (I assume that it is 9). That points to the line
if elem['name'] == 'Stuart':
如果这不能给你一个线索,我在这种情况下推荐的方法是开始添加一些 print
函数,这样你就知道你在做什么.for
循环如下所示:
And if that doesn't give you a clue, the approach that I recommend in such cases is starting to add some print
functions, so that you know what you are working on. The for
loop looks like:
for elem in data:
print('elem', elem)
if elem['name'] == 'Stuart':
print('elem->hobbies', elem['hobbies'])
elem['hobbies'] = ['Fishing']
打印出来
elem team_member
在抛出异常之前,我希望这会让你意识到你不是在迭代列表的 elements(项目),而是在 keys 的 dict(从 YAML 中的根级别映射构造).而与键关联的值是具有键name
和键hobbies
的对象.
before the exception is thrown, and I hope that will make you realize your are not iterating over the elements (items) of a list, but over the keys of a dict (constructed from the root level mapping in your YAML). And the value associated with the key is the object having a key name
and a key hobbies
.
因此,将变量 elem
更改为 key
以明确您正在处理的内容,然后继续使用 value
,关联的值在该循环中使用该键而不是 elem
¹:
So change the variable elem
to key
to make clear what you're handling and then proceed to work with value
, the value associated with that key instead of elem
within that loop¹:
for key in data:
value = data[key]
if value['name'] == 'Stuart':
print('value->hobbies', value['hobbies'])
value['hobbies'] = ['Fishing']
这给出了:
value->hobbies ['dancing']
team_member:
name: Max
hobbies:
- Reading
team_leader:
name: Stuart
hobbies:
- Fishing
所以我们摆脱了异常,但结果并不完全是您想要的.键爱好"的元素 dancing
消失了,因为您为该键分配了一个新的(列表)值,而您应该做的是将单个项目附加到列表中.我们现在也可以去掉打印功能了:
So we got rid of the exception, but the result is not exactly what you want. The element dancing
for the key 'hobbies' is gone, because you assign a new (list) value to that key, whereas what you should do is append a single item to the list. We can also get rid of the print function by now:
for key in data:
value = data[key]
if value['name'] == 'Stuart':
value['hobbies'].append('Fishing')
这将在文件的最终序列中为您提供两个项目.还有一些问题需要解决:
This will get you two items in the final sequence in the file. There is a few more things to address:
dancing
的大写不正确.要纠正这个问题,如果只有一个元素,请添加一行处理列表- 需要添加名称
Max
的代码(这就是为什么您需要去掉代码中的break
) - 空行,被认为是对第一个序列的最后一个元素的注释,需要移动该注释
- 您的序列缩进不是默认的
- the capitalization of
dancing
incorrect. To correct that, add a line handling the list if there is only one element - the code for the name
Max
, needs to be added (and that is why you need to get rid of thebreak
in your code) - the empty line, is considered a comment on the last element of the first sequence, that comment needs to be moved
- your indentation of sequences is non-default
最终代码如下:
from pathlib import Path
import ruamel.yaml
path = Path('input.yaml')
yaml = ruamel.yaml.YAML()
yaml.indent(sequence=4, offset=2) # for the non-default indentation of sequences
data = yaml.load(path)
for key in data:
value = data[key]
if value['name'] == 'Stuart':
if len(value['hobbies']) == 1:
value['hobbies'][0] = value['hobbies'][0].capitalize()
value['hobbies'].append('Fishing')
elif value['name'] == 'Max':
last_item_index = len(value['hobbies']) - 1
value['hobbies'].append('Painting')
comments = value['hobbies'].ca
if not comments.items[last_item_index][0].value.strip():
# move empty comment lines from previous last item to new last item
comments.items[last_item_index + 1] = comments.items.pop(last_item_index)
yaml.dump(data, path)
这给出了非常接近你想要得到的东西
Which gives something quite close to what you wanted to get
team_member:
name: Max
hobbies:
- Reading
- Painting
team_leader:
name: Stuart
hobbies:
- Dancing
- Fishing
<小时>
¹前两行的替代方案:for key, value in data.items()
相关文章