ネストされたディクショナリをウォークすると再帰が必要となり、 "prefix"を "path"に渡すことで、パスのセグメント(@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
もしあなた入力に二重パスがない場合、出力には何もありませんリスト。 ruamel.yaml
からstring_types
とordereddict
を使用することにより
は(あなたが使用しているバージョンを示していない)、このPython2とのpython3への互換性が高まります。
ordereddictは元のキーの順序を保持しますが、これはもちろんファイルの処理順序に依存します。あなたはパスがソートしたい場合は、単に使用するdump()
を変更:
for path in sorted(self._result):
はまた、上のコメントは、辞書のエントリが保存されている「コメント」ことに注意してください。
は ruamel.yamlはラウンドトリップにコメントや他のデータを保存YAML 1.2パーサー(PyYAMLとはYAML 1.1の大部分を行う)である¹しました。免責事項:私はruamel.yamlの著者です
これは、私の意見が持っている可能性のある多くの重要なケースに対する非常によく考えられた解決策です。ありがとうございます。私は特にそれがコメントを保存するのが好きです。 – swellactually