是否可以在 YAML 中进行字符串替换?
Is it possible to do string substitution in YAML?
有没有办法在 YAML 中替换字符串。例如,我想定义一次 sub
并在整个 YAML 文件中使用它。
sub: ['a', 'b', 'c']
command:
params:
cmd1:
type: string
enum : # Get the list defined in 'sub'
description: Exclude commands from the test list.
cmd2:
type: string
enum: # Get the list defined in 'sub'
您不能真正替换 YAML 中的字符串值,例如用另一个子字符串替换某个字符串的子字符串¹。然而,YAML 确实有可能标记一个节点(在您的情况下,列表 ['a'、'b'、'c'] 带有 anchor and reuse that as an alias node.
锚采用 &some_id
形式并插入到节点之前,别名节点由 *some_id
(而不是节点)指定。
这与字符串级别的替换不同,因为在解析 YAML 文件期间可以保留引用。就像在 Python 中为集合类型上的任何锚点加载 YAML 时的情况一样(即 而不是 在标量上使用锚点时):
import sys
import ruamel.yaml as yaml
yaml_str = """\
sub: &sub0 [a, b, c]
command:
params:
cmd1:
type: string
# Get the list defined in 'sub'
enum : *sub0
description: Exclude commands from the test list.
cmd2:
type: string
# Get the list defined in 'sub'
enum: *sub0
"""
data1 = yaml.load(yaml_str, Loader=yaml.RoundTripLoader)
# the loaded elements point to the same list
assert data1['sub'] is data1['command']['params']['cmd1']['enum']
# change in cmd2
data1['command']['params']['cmd2']['enum'][3] = 'X'
yaml.dump(data1, sys.stdout, Dumper=yaml.RoundTripDumper, indent=4)
这将输出:
sub: &sub0 [a, X, c]
command:
params:
cmd1:
type: string
# Get the list defined in 'sub'
enum: *sub0
description: Exclude commands from the test list.
cmd2:
type: string
# Get the list defined in 'sub'
enum: *sub0
请注意,原始锚点名称在 ruamel.yaml 中保留²。
如果您不想在输出中使用锚点和别名,您可以覆盖 RoundTripDumper
的 RoundTripRepresenter
子类中的 ignore_aliases
方法(该方法有两个参数,但是使用 lambda *args: ....
你不必知道):
dumper = yaml.RoundTripDumper
dumper.ignore_aliases = lambda *args : True
yaml.dump(data1, sys.stdout, Dumper=dumper, indent=4)
给出:
sub: [a, X, c]
command:
params:
cmd1:
type: string
# Get the list defined in 'sub'
enum: [a, X, c]
description: Exclude commands from the test list.
cmd2:
type: string
# Get the list defined in 'sub'
enum: [a, X, c]
这个技巧可用于读取 YAML 文件,就好像您已完成字符串替换一样,方法是重新读取您在忽略别名时转储的 material:
data2 = yaml.load(yaml.dump(yaml.load(yaml_str, Loader=yaml.RoundTripLoader),
Dumper=dumper, indent=4), Loader=yaml.RoundTripLoader)
# these are lists with the same value
assert data2['sub'] == data2['command']['params']['cmd1']['enum']
# but the loaded elements do not point to the same list
assert data2['sub'] is not data2['command']['params']['cmd1']['enum']
data2['command']['params']['cmd2']['enum'][5] = 'X'
yaml.dump(data2, sys.stdout, Dumper=yaml.RoundTripDumper, indent=4)
现在只有一个'b'
变成了'X'
:
sub: [a, b, c]
command:
params:
cmd1:
type: string
# Get the list defined in 'sub'
enum: [a, b, c]
description: Exclude commands from the test list.
cmd2:
type: string
# Get the list defined in 'sub'
enum: [a, X, c]
如上所述,只有在集合类型上使用 anchors/aliases 时才需要这样做,而在标量上使用它时则不需要。
¹ 由于 YAML 可以创建对象,因此可以影响
解析器(如果创建了这些对象)。 This answer 描述了如何做到这一点。
² 保留名称最初是不可用的,但在 ruamel.yaml
的更新中实现了
有没有办法在 YAML 中替换字符串。例如,我想定义一次 sub
并在整个 YAML 文件中使用它。
sub: ['a', 'b', 'c']
command:
params:
cmd1:
type: string
enum : # Get the list defined in 'sub'
description: Exclude commands from the test list.
cmd2:
type: string
enum: # Get the list defined in 'sub'
您不能真正替换 YAML 中的字符串值,例如用另一个子字符串替换某个字符串的子字符串¹。然而,YAML 确实有可能标记一个节点(在您的情况下,列表 ['a'、'b'、'c'] 带有 anchor and reuse that as an alias node.
锚采用 &some_id
形式并插入到节点之前,别名节点由 *some_id
(而不是节点)指定。
这与字符串级别的替换不同,因为在解析 YAML 文件期间可以保留引用。就像在 Python 中为集合类型上的任何锚点加载 YAML 时的情况一样(即 而不是 在标量上使用锚点时):
import sys
import ruamel.yaml as yaml
yaml_str = """\
sub: &sub0 [a, b, c]
command:
params:
cmd1:
type: string
# Get the list defined in 'sub'
enum : *sub0
description: Exclude commands from the test list.
cmd2:
type: string
# Get the list defined in 'sub'
enum: *sub0
"""
data1 = yaml.load(yaml_str, Loader=yaml.RoundTripLoader)
# the loaded elements point to the same list
assert data1['sub'] is data1['command']['params']['cmd1']['enum']
# change in cmd2
data1['command']['params']['cmd2']['enum'][3] = 'X'
yaml.dump(data1, sys.stdout, Dumper=yaml.RoundTripDumper, indent=4)
这将输出:
sub: &sub0 [a, X, c]
command:
params:
cmd1:
type: string
# Get the list defined in 'sub'
enum: *sub0
description: Exclude commands from the test list.
cmd2:
type: string
# Get the list defined in 'sub'
enum: *sub0
请注意,原始锚点名称在 ruamel.yaml 中保留²。
如果您不想在输出中使用锚点和别名,您可以覆盖 RoundTripDumper
的 RoundTripRepresenter
子类中的 ignore_aliases
方法(该方法有两个参数,但是使用 lambda *args: ....
你不必知道):
dumper = yaml.RoundTripDumper
dumper.ignore_aliases = lambda *args : True
yaml.dump(data1, sys.stdout, Dumper=dumper, indent=4)
给出:
sub: [a, X, c]
command:
params:
cmd1:
type: string
# Get the list defined in 'sub'
enum: [a, X, c]
description: Exclude commands from the test list.
cmd2:
type: string
# Get the list defined in 'sub'
enum: [a, X, c]
这个技巧可用于读取 YAML 文件,就好像您已完成字符串替换一样,方法是重新读取您在忽略别名时转储的 material:
data2 = yaml.load(yaml.dump(yaml.load(yaml_str, Loader=yaml.RoundTripLoader),
Dumper=dumper, indent=4), Loader=yaml.RoundTripLoader)
# these are lists with the same value
assert data2['sub'] == data2['command']['params']['cmd1']['enum']
# but the loaded elements do not point to the same list
assert data2['sub'] is not data2['command']['params']['cmd1']['enum']
data2['command']['params']['cmd2']['enum'][5] = 'X'
yaml.dump(data2, sys.stdout, Dumper=yaml.RoundTripDumper, indent=4)
现在只有一个'b'
变成了'X'
:
sub: [a, b, c]
command:
params:
cmd1:
type: string
# Get the list defined in 'sub'
enum: [a, b, c]
description: Exclude commands from the test list.
cmd2:
type: string
# Get the list defined in 'sub'
enum: [a, X, c]
如上所述,只有在集合类型上使用 anchors/aliases 时才需要这样做,而在标量上使用它时则不需要。
¹ 由于 YAML 可以创建对象,因此可以影响
解析器(如果创建了这些对象)。 This answer 描述了如何做到这一点。
² 保留名称最初是不可用的,但在 ruamel.yaml