我正在努力将多个.yaml
文件中存在的所有文字放入一个新的单一YAML文件中,该文件将包含有人可以翻译成西班牙文的英文翻译。
每个YAML文件都有很多嵌套文本。我想为YAML文件中的每个值打印完整的路径',也就是所有键以及值。以下是myproject.section.more_information文件中.yaml
文件的示例输入:
default:
heading: Here’s A Title
learn_more:
title: Title of Thing
url: www.url.com
description: description
opens_new_window: true
并且这里是所需的输出:
myproject.section.more_information.default.heading: Here’s a Title
myproject.section.more_information.default.learn_more.title: Title of Thing
mproject.section.more_information.default.learn_more.url: www.url.com
myproject.section.more_information.default.learn_more.description: description
myproject.section.more_information.default.learn_more.opens_new_window: true
这似乎是递归的一个很好的候选者,因此我查看了this answer
等示例但是,我希望保留导致给定值的所有键,而不仅仅是值中的最后一个键。我目前正在使用PyYAML来读/写YAML。
有关如何保存每个密钥的任何提示,我继续检查该项是否为字典,然后返回与每个值关联的所有密钥?
答案 0 :(得分:1)
您想要做的是拼合嵌套词典。这将是一个很好的起点:Flatten nested Python dictionaries, compressing keys
事实上,如果您刚刚将sep参数更改为.
,我认为顶部答案中的代码段对您有用。
编辑:
根据链接的SO回答http://ideone.com/Sx625B
检查这个工作示例import collections
some_dict = {
'default': {
'heading': 'Here’s A Title',
'learn_more': {
'title': 'Title of Thing',
'url': 'www.url.com',
'description': 'description',
'opens_new_window': 'true'
}
}
}
def flatten(d, parent_key='', sep='_'):
items = []
for k, v in d.items():
new_key = parent_key + sep + k if parent_key else k
if isinstance(v, collections.MutableMapping):
items.extend(flatten(v, new_key, sep=sep).items())
else:
items.append((new_key, v))
return dict(items)
results = flatten(some_dict, parent_key='', sep='.')
for item in results:
print(item + ': ' + results[item])
如果你想按顺序排列,那么你需要一个OrderedDict。
答案 1 :(得分:0)
保留一个简单的字符串列表,作为每个缩进深度的最新键。当您从一行进展到下一行而没有任何变化时,只需更改列表末尾的项目即可。当您“失败”时,弹出列表中的最后一项。缩进时,请附加到列表中。
然后,每次敲击冒号时,相应的键项是列表中字符串的串联,如:
'.'.join(key_list)
这会让你以一种光荣的速度前进吗?
答案 2 :(得分:0)
遍历嵌套字典需要递归,并通过将“前缀”交给“路径”,这可以防止您对路径的各个部分进行任何操作(如@Prune所示)。
要记住一些事情会让这个问题变得有趣:
.
的键)。我的例子报告了这些并退出。使用ruamel.yaml¹的示例代码:
import sys
import glob
import ruamel.yaml
from ruamel.yaml.comments import CommentedMap, CommentedSeq
from ruamel.yaml.compat import string_types, ordereddict
class Flatten:
def __init__(self, base):
self._result = ordereddict() # key to list of tuples of (value, comment)
self._base = base
def add(self, file_name):
data = ruamel.yaml.round_trip_load(open(file_name))
self.walk_tree(data, self._base)
def walk_tree(self, data, prefix=None):
"""
this is based on ruamel.yaml.scalarstring.walk_tree
"""
if prefix is None:
prefix = ""
if isinstance(data, dict):
for key in data:
full_key = self.full_key(key, prefix)
value = data[key]
if isinstance(value, (dict, list)):
self.walk_tree(value, full_key)
continue
# value is a scalar
comment_token = data.ca.items.get(key)
comment = comment_token[2].value if comment_token else None
self._result.setdefault(full_key, []).append((value, comment))
elif isinstance(base, list):
print("don't know how to handle lists", prefix)
sys.exit(1)
def full_key(self, key, prefix):
"""
check here for valid keys
"""
if not isinstance(key, string_types):
print('key has to be string', repr(key), prefix)
sys.exit(1)
if '.' in key:
print('dot in key not allowed', repr(key), prefix)
sys.exit(1)
if key == '':
print('empty key not allowed', repr(key), prefix)
sys.exit(1)
return prefix + '.' + key
def dump(self, out):
res = CommentedMap()
for path in self._result:
values = self._result[path]
if len(values) == 1: # single value for path
res[path] = values[0][0]
if values[0][1]:
res.yaml_add_eol_comment(values[0][1], key=path)
continue
res[path] = seq = CommentedSeq()
for index, value in enumerate(values):
seq.append(value[0])
if values[0][1]:
res.yaml_add_eol_comment(values[0][1], key=index)
ruamel.yaml.round_trip_dump(res, out)
flatten = Flatten('myproject.section.more_information')
for file_name in glob.glob('*.yaml'):
flatten.add(file_name)
flatten.dump(sys.stdout)
如果您有其他输入文件:
default:
learn_more:
commented: value # this value has a comment
description: another description
然后结果是:
myproject.section.more_information.default.heading: Here’s A Title
myproject.section.more_information.default.learn_more.title: Title of Thing
myproject.section.more_information.default.learn_more.url: www.url.com
myproject.section.more_information.default.learn_more.description:
- description
- another description
myproject.section.more_information.default.learn_more.opens_new_window: true
myproject.section.more_information.default.learn_more.commented: value # this value has a comment
当然,如果您的输入没有双路径,那么您的输出将没有任何列表。
使用string_types
中的ordereddict
和ruamel.yaml
使得此Python2和Python3兼容(您没有指明您使用的是哪个版本)。
ordereddict保留原始密钥排序,但这当然取决于文件的处理顺序。如果您希望对路径进行排序,只需将dump()
更改为使用:
for path in sorted(self._result):
另请注意,“已评论”字典条目的注释会被保留。
¹ ruamel.yaml是一个YAML 1.2解析器,它保存有关往返的注释和其他数据(PyYAML可以处理YAML 1.1的大部分内容)。免责声明:我是ruamel.yaml的作者