无法将YAML与评论合并

我正在做一个工具来自动化一些工作,我需要合并一些configurationYAML,但我需要注释,因为我需要描述未来的领域。
我已经设法做到这一点没有评论,将YAML转换为JSON,再次合并和转换为YAML。 我愿意使用XML或其他的东西,因为我可以在本地运行它。 任何人都知道任何可以帮助我?

喜欢这个:

文件1

project: general: environment: ? databases: # Main Database db1: host: localhost username: root password: root123 dbname: project logFile: ? 

文件2:

 project: general: environment: local databases: db1: # New Log File logFile: project.log 

会导致这样的结果:

 project: general: environment: local databases: # Main Database db1: host: localhost username: root password: root123 dbname: project # New Log File logFile: project.log 

作为@flyx表示你应该看看ruamel.yaml (免责声明:我是该包的作者)的往返能力,尽pipe没有内置的recursion合并,并且有一些注意事项。

首先你应该引用你的? 值,否则你会得到一个警告,映射键不被允许(作为一个普通的?通常会引入一个明确定义的映射键)。

同样重要的是要知道ruamel.yaml中的注释的ruamel.yaml往往是在注释之前的最后parsing的节点。 所以在你的file2.yaml # New Log File注释与前面的密钥db1关联,而不是以下的logFile

如果你愿意这样inputfile1.yaml

 project: general: environment: '?' databases: # Main Database db1: host: localhost username: root password: root123 dbname: project logFile: '?' 

file2.yaml一样:

 project: general: environment: local databases: db1: logFile: project.log # New Log File 

那么这个程序:

 import sys from pathlib import Path import ruamel.yaml def update(d, n): if isinstance(n, ruamel.yaml.comments.CommentedMap): for k in n: d[k] = update(d[k], n[k]) if k in d else n[k] if k in n.ca._items and n.ca._items[k][2] and \ n.ca._items[k][2].value.strip(): d.ca._items[k] = n.ca._items[k] # copy non-empty comment else: d = n return d data1 = ruamel.yaml.round_trip_load(Path('file1.yaml').read_text()) update(data1, ruamel.yaml.round_trip_load(Path('file2.yaml').read_text())) ruamel.yaml.round_trip_dump(data1, sys.stdout) 

足以给你以下输出:

 project: general: environment: local databases: # Main Database db1: host: localhost username: root password: root123 dbname: project logFile: project.log # New Log File 

请注意, logFile: '?'不是必须的logFile: '?'file1.txt ,因为遗漏的键将被添加在映射的末尾。

如果在密钥不可接受的情况下将# New Log File移动到file2.yaml ,则必须预先处理来自file2.yaml的加载数据,这在这种情况下并不困难。 这样做的基础上,例如取决于您的原始file2.yaml的缩进是可能的,但需要相当多的代码行才能得到正确的,有点脆弱:

 import sys from pathlib import Path import ruamel.yaml INDENT=4 def update(d, n): if isinstance(n, ruamel.yaml.comments.CommentedMap): for k in n: d[k] = update(d[k], n[k]) if k in d else n[k] if k in n.ca._items and \ ((n.ca._items[k][2] and n.ca._items[k][2].value.strip()) or \ n.ca._items[k][1]): d.ca._items[k] = n.ca._items[k] # copy non-empty comment else: d = n return d def move_comment(d, depth=0): # recursively adjust comment if isinstance(d, ruamel.yaml.comments.CommentedMap): for k in d: if isinstance(d[k], ruamel.yaml.comments.CommentedMap): if hasattr(d, 'ca'): comment = d.ca.items.get(k) if comment and comment[3] is not None: # add to first key of the mapping that is the value for k1 in d[k]: d[k].yaml_set_comment_before_after_key( k1, before=comment[3][0].value.lstrip('#').strip(), indent=INDENT*(depth+1)) break move_comment(d[k], depth+1) return d data1 = ruamel.yaml.round_trip_load(Path('file1.yaml').read_text()) update(data1, move_comment(ruamel.yaml.round_trip_load(Path('file2.yaml').read_text()))) ruamel.yaml.round_trip_dump(data1, sys.stdout, indent=INDENT) 

上面给出了你用正确的( '?'file1.yaml和你原来的file2.yaml要求的输出。

你不能用普通的YAML实现,因为YAML定义了注释是一个expression细节,不能传递内容信息。 因此,只要您parsingYAML,您将自动失去评论信息。

ruamel提供了ruamel.yaml.round_trip_load() 。 这给你一个CommentedMap (如果你的YAML有一个作为根types的映射),它保留所有评论。 您可以按照元素的方式合并这些贴图,然后再将它们输出为YAML。

根据你的YAML的布局,你也可以成功地将它们合并在一个文本的基础上。 例如,对于这样的两个YAML文件:

first.yaml:

 foo: bar spam: egg 

第二:YAML:

 sausage: spam baked: beans 

您可以通过简单地将缩进添加到每行并连接它们来合并它们:

 first: foo: bar spam: egg second: sausage: spam baked: beans 

你只需重复上面的代码就可以了。 这将适用于任何格式正确的inputYAML文件,只要它们没有明确的指令或文档结束标记---... )。

如果你想合并同一级别的YAML文件,你仍然可以尝试连接它们,这与我的例子很好地工作:

 foo: bar spam: egg sausage: spam baked: beans 

您也可以将它们连接成一个包含多个文档的YAML文件,虽然我不确定这是否是你想要的:

 foo: bar spam: egg ... --- sausage: spam baked: beans 

这是保证按照YAML规范工作。