是否可以在 YAML 中进行字符串替换? [英] Is it possible to do string substitution in YAML?

查看:56
本文介绍了是否可以在 YAML 中进行字符串替换?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有办法在 YAML 中替换字符串.例如,我想定义一次 sub 并在整个 YAML 文件中使用它.

sub: ['a', 'b', 'c']命令:参数:cmd1:类型:字符串enum : # 获取'sub'中定义的列表描述:从测试列表中排除命令.cmd2:类型:字符串enum: # 获取'sub'中定义的列表

解决方案

您不能真正替换 YAML 中的字符串值,就像将某个字符串的子字符串替换为另一个子字符串¹一样.然而,YAML 确实有可能标记一个节点(在您的情况下,列表 ['a', 'b', 'c'] 带有 并将其重用为 别名节点.

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

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

导入系统将 ruamel.yaml 导入为 yamlyaml_str = """\sub: &sub0 [a, b, c]命令:参数:cmd1:类型:字符串# 获取'sub'中定义的列表枚举:*sub0描述:从测试列表中排除命令.cmd2:类型:字符串# 获取'sub'中定义的列表枚举:*sub0"""data1 = yaml.load(yaml_str, Loader=yaml.RoundTripLoader)# 加载的元素指向同一个列表断言 data1['sub'] 是 data1['command']['params']['cmd1']['enum']# 改变 cmd2data1['command']['params']['cmd2']['enum'][3] = 'X'yaml.dump(data1, sys.stdout, Dumper=yaml.RoundTripDumper, indent=4)

这将输出:

sub: &sub0 [a, X, c]命令:参数:cmd1:类型:字符串# 获取'sub'中定义的列表枚举:*sub0描述:从测试列表中排除命令.cmd2:类型:字符串# 获取'sub'中定义的列表枚举:*sub0

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

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

dumper = yaml.RoundTripDumperdumper.ignore_aliases = lambda *args : 真yaml.dump(data1, sys.stdout, Dumper=dumper, indent=4)

给出:

sub: [a, X, c]命令:参数:cmd1:类型:字符串# 获取'sub'中定义的列表枚举:[a, X, c]描述:从测试列表中排除命令.cmd2:类型:字符串# 获取'sub'中定义的列表枚举:[a, X, c]

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

data2 = yaml.load(yaml.dump(yaml.load(yaml_str, Loader=yaml.RoundTripLoader),Dumper=dumper, indent=4), Loader=yaml.RoundTripLoader)# 这些是具有相同值的列表assert data2['sub'] == data2['command']['params']['cmd1']['enum']# 但加载的元素没有指向同一个列表断言 data2['sub'] 不是 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]命令:参数:cmd1:类型:字符串# 获取'sub'中定义的列表枚举:[a, b, c]描述:从测试列表中排除命令.cmd2:类型:字符串# 获取'sub'中定义的列表枚举:[a, X, c]

如上所述,这仅在对集合类型使用锚点/别名时才需要,而在标量上使用时则不需要.

<小时>

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

Is there a way to substitute string in YAML. For example, I would like to define sub once and use it throughout the YAML file.

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'

解决方案

You cannot really substitute string values in YAML, as in replacing a substring of some string with another substring¹. YAML does however have a possibility to mark a node (in your case the list ['a', 'b', 'c'] with an anchor and reuse that as an alias node.

Anchors take the form &some_id and are inserted before a node and alias nodes are specified by *some_id (instead of a node).

This is not the same as substitution on the string level, because during parsing of the YAML file the reference can be preserved. As is the case when loading YAML in Python for any anchors on collection types (i.e. not when using anchors on scalars):

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)

This will output:

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

please note that the original anchor name is preserved² in ruamel.yaml.

If you don't want the anchors and aliases in your output you can override the ignore_aliases method in the RoundTripRepresenter subclass of RoundTripDumper (that method takes two arguments, but using lambda *args: .... you don't have to know about that):

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

Which gives:

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]

And this trick can be used to read in the YAML file as if you had done string substitution, by re-reading the material you dumped while ignoring the aliases:

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)

And now only one 'b' is changed into '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]

As indicated above this is only necessary when using anchors/aliases on collection types, not when you use it on a scalar.


¹ Since YAML can create objects, it is however possible to influence the parser if those objects are created. This answer describes how to do that.
² Preserving the names was originally not available, but was implemented in update of ruamel.yaml

这篇关于是否可以在 YAML 中进行字符串替换?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆