我正在开发一个工具来自动化一些工作,我需要将一些配置 YAML 合并到一个中,但我需要注释,因为我需要向 future 描述这些字段。
我已经成功地在没有注释的情况下做到了这一点,将 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
的往返功能(免责声明:我是该包的作者),尽管没有内置的递归合并并且有一些警告。
首先,您应该引用您的 ?
值,否则您会收到一条警告,指出不允许映射键(因为普通的 ?
通常会引入显式定义的映射键)。
还需要了解的是,ruamel.yaml 中的注释关联往往与注释之前的最后一个解析 Node 相关联。因此,在您的 file2.yaml
中,# New Log File
注释与前面的键 db1
关联,而不是与下面的 logFile
。
如果您愿意像这样输入file1.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: '?'
不必位于 file1.txt
中,因为缺少的键将添加到映射末尾。
如果将# New Log File
移动到 key 后面的位置 Not Acceptable ,那么您必须预处理从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
.
关于javascript - 无法将 YAML 与注释合并,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43592168/