是否可以在YAML中进行字符串替换?

Cor*_*ory 8 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'
Run Code Online (Sandbox Code Playgroud)

Ant*_*hon 13

您无法真正替换YAML中的字符串值,例如将某个字符串的子字符串替换为另一个子字符串¹.但是,YAML可以标记一个节点(在您的情况下,使用锚点将列表['a','b','c'] 重新标记,并将其重新用作别名节点.

Anchor采用表单&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)
Run Code Online (Sandbox Code Playgroud)

这将输出:

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
Run Code Online (Sandbox Code Playgroud)

请注意,原始锚名称保留在ruamel.yaml中.

如果你不想在输出中使用锚和别名,你可以覆盖子类中的ignore_aliases方法(该方法需要两个参数,但使用你不需要知道):RoundTripRepresenterRoundTripDumperlambda *args: ....

dumper = yaml.RoundTripDumper
dumper.ignore_aliases = lambda *args : True
yaml.dump(data1, sys.stdout, Dumper=dumper, indent=4)
Run Code Online (Sandbox Code Playgroud)

这使:

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]
Run Code Online (Sandbox Code Playgroud)

这个技巧可以用来读取YAML文件,就好像你已经完成字符串替换一样,通过重新读取你转储的材料而忽略了别名:

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)
Run Code Online (Sandbox Code Playgroud)

现在只有一个'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]
Run Code Online (Sandbox Code Playgroud)

如上所述,只有在集合类型上使用锚点/别名时才需要这样做,而不是在标量上使用它时.


¹ 由于YAML可以创建对象,因此如果创建了这些对象,则可能会影响解析器.这个答案描述了如何做到这一点.
² 保留名称最初不可用,但是在ruamel.yaml的更新中实现