Ruamel.yaml: 在不使用标签的情况下将 yaml 反序列化为 python class 实例
Ruamel.yaml: deserialize yaml into python class instaces without using tags
假设我们有一个包含 .yaml
个文件的文件夹,其中包含 kubernetes 对象,比方说,部署、配置映射和 HPA。
./file1.yaml # {'kind': 'Deployment', ... }, {'kind': 'ConfigMap', ...}
./file2.yaml # {'kind': 'ConfigMap', ... }, {'kind': 'HorizontalPodAutoscaler', ... }
我需要将它们反序列化为适当 class 的实例,但与常规反序列化方法不同,我想避免依赖 YAML 标签,而是通过 YAML 正文进行选择(这就是为什么我有对 register_class()
方法的怀疑)。有一个键 'kind'
应该可以识别正确的 class 实例。
最终目标是解析、修改和转储这些对象(保留注释和格式,所以那些classes将是一个子class CommentedMap 或类似的东西)。
有没有办法ruamel.yaml我怎么把YAML解析成
from ruamel.yaml.comments import CommentedMap
class KubeObjectBase(CommentedMap):
def some_additional_func(self):
pass
class Deployment(KubeObjectBase):
def deployment_method(self):
pass
class ConfigMap(KubeObjectBase):
pass
我不完全确定 YAML 文件的实际外观。在你的例子中 #
之后的部分
YAML 不正确,所以我编了一些东西。
这不会影响处理以获得您想要的内容。只要你有有效的、可加载的 YAML,
只是递归数据并替换条目。
您需要以某种方式将 kind
的值映射到您的实际 classes。如果没有
那么多 classes 只是为 class 字典创建一个字符串,如果你有很多,你应该
扫描您的 Python 文件并自动创建该地图(从 class 名称
或来自某些 class 属性):
import sys
import ruamel.yaml
FA = ruamel.yaml.comments.Format.attrib
from pathlib import Path
file1 = Path('file1.yaml')
file1.write_text("""\
- {'kind': 'Deployment', a: 1}
- kind: ConfigMap
b:
kind: Deployment
c: 3
x: 42
""")
file2 = Path('file2.yaml')
file2.write_text("""\
[
{'kind': 'ConfigMap', d: 4},
{'kind': 'HorizontalPodAutoscaler', e: 5},
]
""")
kob_map = {}
class KubeObjectBase(ruamel.yaml.comments.CommentedMap):
def some_additional_func(self):
pass
def __repr__(self):
return f"{self.__class__.__name__}({', '.join([f'{k}: {v}' for k, v in self.items()])})"
class Deployment(KubeObjectBase):
def deployment_method(self):
pass
kob_map['Deployment'] = Deployment
class ConfigMap(KubeObjectBase):
pass
kob_map['ConfigMap'] = ConfigMap
class HorizontalPodAutoscaler(KubeObjectBase):
pass
kob_map['HorizontalPodAutoscaler'] = HorizontalPodAutoscaler
yaml = ruamel.yaml.YAML()
for v in kob_map.values():
yaml.Representer.add_representer(v, yaml.Representer.represent_dict)
def un_kind(d, map):
if isinstance(d, dict):
for k, v in d.items():
un_kind(v, map)
try:
if 'kind' in v:
# typ = map[v.pop('kind')]
typ = nv = map[v['kind']]
d[k] = typ(v)
setattr(nv, FA, v.fa)
setattr(nv, '_comment_attrib', v.ca)
except TypeError:
pass
elif isinstance(d, list):
for idx, elem in enumerate(d):
un_kind(elem, map)
try:
if 'kind' in elem:
# typ = map[elem.pop('kind')]
typ = map[elem['kind']]
d[idx] = nv = typ(elem)
setattr(nv, FA, elem.fa)
setattr(nv, '_comment_attrib', elem.ca)
except TypeError:
pass
for fn in Path('.').glob('*.yaml'):
data = yaml.load(fn)
print(f'{fn}:')
un_kind(data, kob_map)
print(list(data))
yaml.dump(data, sys.stdout)
给出:
file1.yaml:
[Deployment(kind: Deployment, a: 1), ConfigMap(kind: ConfigMap, b: Deployment(kind: Deployment, c: 3, x: 42))]
- {kind: Deployment, a: 1}
- kind: ConfigMap
b:
kind: Deployment
c: 3
x: 42
file2.yaml:
[ConfigMap(kind: ConfigMap, d: 4), HorizontalPodAutoscaler(kind: HorizontalPodAutoscaler, e: 5)]
[{kind: ConfigMap, d: 4}, {kind: HorizontalPodAutoscaler, e: 5}]
假设我们有一个包含 .yaml
个文件的文件夹,其中包含 kubernetes 对象,比方说,部署、配置映射和 HPA。
./file1.yaml # {'kind': 'Deployment', ... }, {'kind': 'ConfigMap', ...}
./file2.yaml # {'kind': 'ConfigMap', ... }, {'kind': 'HorizontalPodAutoscaler', ... }
我需要将它们反序列化为适当 class 的实例,但与常规反序列化方法不同,我想避免依赖 YAML 标签,而是通过 YAML 正文进行选择(这就是为什么我有对 register_class()
方法的怀疑)。有一个键 'kind'
应该可以识别正确的 class 实例。
最终目标是解析、修改和转储这些对象(保留注释和格式,所以那些classes将是一个子class CommentedMap 或类似的东西)。
有没有办法ruamel.yaml我怎么把YAML解析成
from ruamel.yaml.comments import CommentedMap
class KubeObjectBase(CommentedMap):
def some_additional_func(self):
pass
class Deployment(KubeObjectBase):
def deployment_method(self):
pass
class ConfigMap(KubeObjectBase):
pass
我不完全确定 YAML 文件的实际外观。在你的例子中 #
之后的部分
YAML 不正确,所以我编了一些东西。
这不会影响处理以获得您想要的内容。只要你有有效的、可加载的 YAML, 只是递归数据并替换条目。
您需要以某种方式将 kind
的值映射到您的实际 classes。如果没有
那么多 classes 只是为 class 字典创建一个字符串,如果你有很多,你应该
扫描您的 Python 文件并自动创建该地图(从 class 名称
或来自某些 class 属性):
import sys
import ruamel.yaml
FA = ruamel.yaml.comments.Format.attrib
from pathlib import Path
file1 = Path('file1.yaml')
file1.write_text("""\
- {'kind': 'Deployment', a: 1}
- kind: ConfigMap
b:
kind: Deployment
c: 3
x: 42
""")
file2 = Path('file2.yaml')
file2.write_text("""\
[
{'kind': 'ConfigMap', d: 4},
{'kind': 'HorizontalPodAutoscaler', e: 5},
]
""")
kob_map = {}
class KubeObjectBase(ruamel.yaml.comments.CommentedMap):
def some_additional_func(self):
pass
def __repr__(self):
return f"{self.__class__.__name__}({', '.join([f'{k}: {v}' for k, v in self.items()])})"
class Deployment(KubeObjectBase):
def deployment_method(self):
pass
kob_map['Deployment'] = Deployment
class ConfigMap(KubeObjectBase):
pass
kob_map['ConfigMap'] = ConfigMap
class HorizontalPodAutoscaler(KubeObjectBase):
pass
kob_map['HorizontalPodAutoscaler'] = HorizontalPodAutoscaler
yaml = ruamel.yaml.YAML()
for v in kob_map.values():
yaml.Representer.add_representer(v, yaml.Representer.represent_dict)
def un_kind(d, map):
if isinstance(d, dict):
for k, v in d.items():
un_kind(v, map)
try:
if 'kind' in v:
# typ = map[v.pop('kind')]
typ = nv = map[v['kind']]
d[k] = typ(v)
setattr(nv, FA, v.fa)
setattr(nv, '_comment_attrib', v.ca)
except TypeError:
pass
elif isinstance(d, list):
for idx, elem in enumerate(d):
un_kind(elem, map)
try:
if 'kind' in elem:
# typ = map[elem.pop('kind')]
typ = map[elem['kind']]
d[idx] = nv = typ(elem)
setattr(nv, FA, elem.fa)
setattr(nv, '_comment_attrib', elem.ca)
except TypeError:
pass
for fn in Path('.').glob('*.yaml'):
data = yaml.load(fn)
print(f'{fn}:')
un_kind(data, kob_map)
print(list(data))
yaml.dump(data, sys.stdout)
给出:
file1.yaml:
[Deployment(kind: Deployment, a: 1), ConfigMap(kind: ConfigMap, b: Deployment(kind: Deployment, c: 3, x: 42))]
- {kind: Deployment, a: 1}
- kind: ConfigMap
b:
kind: Deployment
c: 3
x: 42
file2.yaml:
[ConfigMap(kind: ConfigMap, d: 4), HorizontalPodAutoscaler(kind: HorizontalPodAutoscaler, e: 5)]
[{kind: ConfigMap, d: 4}, {kind: HorizontalPodAutoscaler, e: 5}]