在键中使用点分隔符解析 YAML
Parse YAML with dots delimiter in keys
我们使用 YAML 配置来扩展服务。通常它是这样的:
service:
scalingPolicy:
capacity:
min: 1
max: 1
所以很容易用基本的 PyYAML 打开并作为字典解析以获得 config['service']['scalingPolicy']['capacity']['min']
结果 1
。问题是一些配置是用点分隔符构建的,例如:
service.scalingPolicy.capacity:
min: 1
max: 1
此配置的基本使用者是 Java 的 Spring,不知何故,它与上面的示例一样受到同等对待。但由于还需要使用 Python 解析这些配置 - 我将整个点分隔线作为 config['service.scalingPolicy.capacity']
键。
问题是 - 我将如何让 python 解析任何类型的键组合(separated by dots
并由 tabulation and :
分隔)。我没有找到 Python YAML 库的相关参数(我已经检查了标准 PyYAML 和 ruamel.yaml
)并且手动处理任何可能的组合似乎是一个疯狂的想法。我唯一可能的想法是编写自己的解析器,但也许我缺少某些东西,这样我就不必重新发明自行车了。
这不是微不足道的,使用键拆分查找要容易得多
点递归到嵌套数据结构中。在这里你有一个嵌套
数据结构和不同的 [key]
查找意味着不同的事情
在不同的层次。
如果你在默认的round-trip模式下使用ruamel.yaml
,你可以添加一个class-variable
到表示映射的类型,定义键被拆分的内容和实例变量
跟踪已经匹配的前缀:
import sys
import ruamel.yaml
from ruamel.yaml.compat import ordereddict
from ruamel.yaml.comments import merge_attrib
yaml_str = """\
service.scalingPolicy.capacity:
min: 1
max: 1
"""
def mapgetitem(self, key):
sep = getattr(ruamel.yaml.comments.CommentedMap, 'sep')
if sep is not None:
if not hasattr(self, 'splitprefix'):
self.splitprefix = ''
if self.splitprefix:
self.splitprefix += sep + key
else:
self.splitprefix = key
if self.splitprefix not in self:
for k in self.keys():
if k.startswith(self.splitprefix):
break
else:
raise KeyError(self.splitprefix)
return self
key = self.splitprefix
delattr(self, 'splitprefix') # to make the next lookup work from start
try:
return ordereddict.__getitem__(self, key)
except KeyError:
for merged in getattr(self, merge_attrib, []):
if key in merged[1]:
return merged[1][key]
raise
old_mapgetitem = ruamel.yaml.comments.CommentedMap.__getitem__ # save the original __getitem__
ruamel.yaml.comments.CommentedMap.__getitem__ = mapgetitem
ruamel.yaml.comments.CommentedMap.sep = '.'
yaml = ruamel.yaml.YAML()
# yaml.indent(mapping=4, sequence=4, offset=2)
# yaml.preserve_quotes = True
config = yaml.load(yaml_str)
print('min:', config['service']['scalingPolicy']['capacity']['min'])
print('max:', config['service']['scalingPolicy']['capacity']['max'])
print('---------')
config['service']['scalingPolicy']['capacity']['max'] = 42
# and dump with the original routine, as it uses __getitem__
ruamel.yaml.comments.CommentedMap.__getitem__ = old_mapgetitem
yaml.dump(config, sys.stdout)
给出:
min: 1
max: 1
---------
service.scalingPolicy.capacity:
min: 1
max: 42
我们使用 YAML 配置来扩展服务。通常它是这样的:
service:
scalingPolicy:
capacity:
min: 1
max: 1
所以很容易用基本的 PyYAML 打开并作为字典解析以获得 config['service']['scalingPolicy']['capacity']['min']
结果 1
。问题是一些配置是用点分隔符构建的,例如:
service.scalingPolicy.capacity:
min: 1
max: 1
此配置的基本使用者是 Java 的 Spring,不知何故,它与上面的示例一样受到同等对待。但由于还需要使用 Python 解析这些配置 - 我将整个点分隔线作为 config['service.scalingPolicy.capacity']
键。
问题是 - 我将如何让 python 解析任何类型的键组合(separated by dots
并由 tabulation and :
分隔)。我没有找到 Python YAML 库的相关参数(我已经检查了标准 PyYAML 和 ruamel.yaml
)并且手动处理任何可能的组合似乎是一个疯狂的想法。我唯一可能的想法是编写自己的解析器,但也许我缺少某些东西,这样我就不必重新发明自行车了。
这不是微不足道的,使用键拆分查找要容易得多
点递归到嵌套数据结构中。在这里你有一个嵌套
数据结构和不同的 [key]
查找意味着不同的事情
在不同的层次。
如果你在默认的round-trip模式下使用ruamel.yaml
,你可以添加一个class-variable
到表示映射的类型,定义键被拆分的内容和实例变量
跟踪已经匹配的前缀:
import sys
import ruamel.yaml
from ruamel.yaml.compat import ordereddict
from ruamel.yaml.comments import merge_attrib
yaml_str = """\
service.scalingPolicy.capacity:
min: 1
max: 1
"""
def mapgetitem(self, key):
sep = getattr(ruamel.yaml.comments.CommentedMap, 'sep')
if sep is not None:
if not hasattr(self, 'splitprefix'):
self.splitprefix = ''
if self.splitprefix:
self.splitprefix += sep + key
else:
self.splitprefix = key
if self.splitprefix not in self:
for k in self.keys():
if k.startswith(self.splitprefix):
break
else:
raise KeyError(self.splitprefix)
return self
key = self.splitprefix
delattr(self, 'splitprefix') # to make the next lookup work from start
try:
return ordereddict.__getitem__(self, key)
except KeyError:
for merged in getattr(self, merge_attrib, []):
if key in merged[1]:
return merged[1][key]
raise
old_mapgetitem = ruamel.yaml.comments.CommentedMap.__getitem__ # save the original __getitem__
ruamel.yaml.comments.CommentedMap.__getitem__ = mapgetitem
ruamel.yaml.comments.CommentedMap.sep = '.'
yaml = ruamel.yaml.YAML()
# yaml.indent(mapping=4, sequence=4, offset=2)
# yaml.preserve_quotes = True
config = yaml.load(yaml_str)
print('min:', config['service']['scalingPolicy']['capacity']['min'])
print('max:', config['service']['scalingPolicy']['capacity']['max'])
print('---------')
config['service']['scalingPolicy']['capacity']['max'] = 42
# and dump with the original routine, as it uses __getitem__
ruamel.yaml.comments.CommentedMap.__getitem__ = old_mapgetitem
yaml.dump(config, sys.stdout)
给出:
min: 1
max: 1
---------
service.scalingPolicy.capacity:
min: 1
max: 42