我最近偶然发现了php的parse_yaml
行为,其中使用YAML中的锚点引用定义的部分作为PHP数组中的引用返回,给出了这种行为:
$yaml = <<<YAML
a: &foo bar
b: *foo
YAML;
$arr = yaml_parse($yaml);
echo $arr["b"]; // returns "bar" as expected
// but when I update $arr["a"]:
$arr["a"] = "baz";
// $arr["b"] is also updated - because it's a reference!
echo $arr["b"]; // returns "baz"!
这很好,一切都很好,但是现在对于我的应用程序,我需要将这些引用展平,以便可以分别更改值。
对此我确实有不好的解决方案,但是有一个好的解决方案吗?
这是我目前使用的错误解决方案:
$yaml = <<<YAML
a: &foo bar
b: *foo
YAML;
$arr = yaml_parse(yaml_emit(yaml_parse($yaml))); // yaml_emit doesn't emit anchors/references
$arr["a"] = "baz";
echo $arr["b"]; // returns "bar"
答案 0 :(得分:1)
如果您的输入在文件test.yaml
中:
a: &foo bar # hello
b: *foo
然后使用以下程序加载和转储该文件,并在可以扩展YAML的情况下扩展它(即,递归数据无法展平)。
import sys
from pathlib import Path
import ruamel.yaml
def null_op(*args, **kw):
return True
# prevent anchors from being preserved even if there are no aliases for them
ruamel.yaml.comments.CommentedBase.yaml_set_anchor = null_op
ruamel.yaml.scalarstring.ScalarString.yaml_set_anchor = null_op
ruamel.yaml.scalarint.ScalarInt.yaml_set_anchor = null_op
ruamel.yaml.scalarfloat.ScalarFloat.yaml_set_anchor = null_op
ruamel.yaml.scalarbool.ScalarBoolean.yaml_set_anchor = null_op
# backup the original file if not backed up yet
yaml_file = Path('test.yaml')
backup = yaml_file.with_suffix('.yaml.org')
if not backup.exists():
backup.write_bytes(yaml_file.read_bytes())
yaml = ruamel.yaml.YAML()
# yaml.indent(mapping=4, sequence=4, offset=2)
yaml.preserve_quotes = True
yaml.representer.ignore_aliases = null_op
data = yaml.load(yaml_file)
yaml.dump(data, yaml_file)
给出:
a: bar # hello
b: bar # hello
必须替换yaml_set_anchor
方法,否则输出
在具有锚或别名的地方都将具有原始锚。
如您所见,如果您对锚定数据有评论,则将其复制(并保留 原始开始列)。别名后的所有注释都会消失。那不是 影响加载的数据的语义,应该没有问题。