您正在尝试更改YAML的字符串表示级别,我认为您不应该这样做。YAML可以加载对象,并且这些对象可以通过钩入解析器来影响后面加载的元素。这样,您可以使用数据替换完整节点,更改标量内的值等。
假设您有这个YAML文件
main.yml
:
- !YAMLPreProcessor
verbose: '3'
escape: ♦
- ♦replace(verbose)
- abcd
- ♦include(xyz.yml)
- xyz
并且 xyz.yml
是:
k: 9
l: 8
m: [7. 6]
如果你有一个特殊字符♦
(它可以是任何东西,只要YAMLPreProcessor值与操作关键字(replace
和include
)的开头匹配),你希望这个字符被回传(加载到内存中的数据,然后转储到以下YAML中:
- !YAMLPreProcessor
verbose: '3'
escape: ♦
- '3'
- abcd
- k: 9
l: 8
m: [7. 6]
- xyz
您可以通过重载标量构造函数并使用适当的YAMLPreProcessor
类来实现此功能:
from __future__ import print_function
import ruamel.yaml as yaml
def construct_scalar(loader, node):
self = getattr(loader, '_yaml_preprocessor', None)
if self and self.d.get('escape'):
if node.value and node.value.startswith(self.d['escape']):
key_word, rest = node.value[1:].split('(', 1)
args, rest = rest.split(')', 1)
if key_word == 'replace':
res = u''
for arg in args.split(','):
res += str(self.d[arg])
node.value = res + rest
elif key_word == 'include':
inc_yml = yaml.load(
open(args),
Loader=yaml.RoundTripLoader
)
return inc_yml
else:
print('keyword not found:', key_word)
ret_val = loader._org_construct_scalar(node)
return ret_val
class YAMLPreProcessor:
def __init__(self, escape=None, verbose=0):
self.d = dict(escape=escape, verbose=verbose)
def __repr__(self):
return "YAMLPreProcessor({escape!r}, {verbose})".format(**self.d)
@staticmethod
def __yaml_out__(dumper, self):
return dumper.represent_mapping('!YAMLPreProcessor', self.d)
@staticmethod
def __yaml_in__(loader, data):
from ruamel.yaml.comments import CommentedMap
result = YAMLPreProcessor()
loader._yaml_preprocessor = result
z = dict()
loader.construct_mapping(data, z)
result.d = z
yield result
def __delete__(self):
loader._yaml_preprocessor = None
def construct_yaml_str(self, node):
value = self.construct_scalar(node)
if isinstance(value, ScalarString):
return value
if PY3:
return value
try:
return value.encode('ascii')
except AttributeError:
return value
except UnicodeEncodeError:
return value
loader = yaml.RoundTripLoader
loader.add_constructor('!YAMLPreProcessor', YAMLPreProcessor.__yaml_in__)
loader._org_construct_scalar = loader.construct_scalar
loader.construct_scalar = construct_scalar
data_from_yaml = yaml.load(open('main.yml'), Loader=loader)
dumper = yaml.RoundTripDumper
dumper.add_representer(YAMLPreProcessor, YAMLPreProcessor.__yaml_out__)
print(yaml.dump(data_from_yaml, Dumper=dumper, allow_unicode=True))
上述内容需要最新版本的ruamel.yaml(0.9.6),因为旧版本 choke if construct_scalar 返回非字符串对象。
请注意,注释在具有
m
键的行后面的位置是相对于该行开头的,而在示例中,没有对插入
xyz.yml
文件的节点的缩进级别进行补偿。