【问题标题】:Is it possible to do string substitution in YAML?是否可以在 YAML 中进行字符串替换?
【发布时间】:2015-08-05 02:43:17
【问题描述】:

有没有办法在 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


【解决方案1】:

您不能真正替换 YAML 中的字符串值,例如将某个字符串的子字符串替换为另一个子字符串¹。但是,YAML 确实可以使用anchor 标记节点(在您的情况下,列表 ['a', 'b', 'c'] 并将其作为alias node 重复使用。

锚点采用&some_id 的形式并插入在*some_id(而不是节点)指定的节点和别名节点之前。

这与字符串级别的替换不同,因为在解析 YAML 文件期间可以保留引用。就像在 Python 中为集合类型上的任何锚加载 YAML 时一样(即在标量上使用锚时 not):

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 中。

如果您不希望在输出中使用锚点和别名,您可以覆盖 RoundTripRepresenterRoundTripDumper 子类中的 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 文件,就像你已经完成了字符串替换一样,通过重新读取你转储的材料而忽略别名:

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]

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


¹ 由于 YAML 可以创建对象,但是可以影响 如果创建了这些对象,则解析器。 This answer 描述了如何做到这一点。
² 保留名称原本不可用,但在更新 ruamel.yaml 时实现

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-25
    • 2021-09-25
    • 1970-01-01
    • 2011-10-04
    • 2016-12-19
    • 1970-01-01
    相关资源
    最近更新 更多