声明数据类型为 ruamel.yaml 这样它就可以 represent/serialize 了吗?
Declare data type to ruamel.yaml so that it can represent/serialize it?
我正在使用 python 库中的一个函数,它 returns 一个具有特定数据类型的对象。我想将该对象序列化为一个 yaml 文件,并且我想使用 ruamel.yaml。问题是 ruamel.yaml
不知道如何序列化函数 returns 并抛出异常的特定数据类型:
RepresenterError: cannot represent an object: <...>
问题是如何将数据类型 "declare" 改为 ruamel.yaml
以便它知道如何处理它。
注意:我不能/我不想对库或任何类似的内容进行更改。我只是 API.
的消费者
为了使这个更具体,让我们使用以下使用 socket.AF_INET
which happens to be an IntEnum
的示例,但具体的数据类型应该不重要。
import sys
import socket
import ruamel.yaml
def third_party_lib():
""" Return a dict with our data """
return {"AF_INET": socket.AF_INET}
yaml = ruamel.yaml.YAML(typ="safe", pure=True)
yaml.dump(third_party_lib(), sys.stdout)
这给出了这个错误:
ruamel.yaml.YAML.dump(self, data, stream, **kw)
File "/home/feanor/Prog/git/vps-bench/.direnv/python-venv-3.7.2/lib/python3.7/site-packages/ruamel/yaml/main.py", line 439, in dump
return self.dump_all([data], stream, _kw, transform=transform)
File "/home/feanor/Prog/git/vps-bench/.direnv/python-venv-3.7.2/lib/python3.7/site-packages/ruamel/yaml/main.py", line 453, in dump_all
self._context_manager.dump(data)
File "/home/feanor/Prog/git/vps-bench/.direnv/python-venv-3.7.2/lib/python3.7/site-packages/ruamel/yaml/main.py", line 801, in dump
self._yaml.representer.represent(data)
File "/home/feanor/Prog/git/vps-bench/.direnv/python-venv-3.7.2/lib/python3.7/site-packages/ruamel/yaml/representer.py", line 84, in represent
node = self.represent_data(data)
File "/home/feanor/Prog/git/vps-bench/.direnv/python-venv-3.7.2/lib/python3.7/site-packages/ruamel/yaml/representer.py", line 111, in represent_data
node = self.yaml_representers[data_types[0]](self, data)
File "/home/feanor/Prog/git/vps-bench/.direnv/python-venv-3.7.2/lib/python3.7/site-packages/ruamel/yaml/representer.py", line 359, in represent_dict
return self.represent_mapping(u'tag:yaml.org,2002:map', data)
File "/home/feanor/Prog/git/vps-bench/.direnv/python-venv-3.7.2/lib/python3.7/site-packages/ruamel/yaml/representer.py", line 222, in represent_mapping
node_value = self.represent_data(item_value)
File "/home/feanor/Prog/git/vps-bench/.direnv/python-venv-3.7.2/lib/python3.7/site-packages/ruamel/yaml/representer.py", line 121, in represent_data
node = self.yaml_representers[None](self, data)
File "/home/feanor/Prog/git/vps-bench/.direnv/python-venv-3.7.2/lib/python3.7/site-packages/ruamel/yaml/representer.py", line 392, in represent_undefined
raise RepresenterError('cannot represent an object: %s' % data)
ruamel.yaml.representer.RepresenterError: cannot represent an object: AddressFamily.AF_INET
为了让ruamel.yaml
能够dump一个特定的class,不管你定义它,你得到它
从标准库或从其他地方获取,你需要 register that class against the representer。
(这在使用 YAML(typ='unsafe')
时不是必需的,但我假设您不想诉诸于此)。
可以通过不同的方式完成此注册。假设你有
完成yaml = ruamel.yaml.YAML()
或yaml = ruamel.yaml.YAML(typ='safe')
,并想代表SomeClass
,你可以:
- 使用
yaml.register_class(SomeClass)
。这可能适用于其他 classes,具体取决于
关于它们是如何定义的。
- 在
class SomeClass:
之前使用装饰器 @yaml_object(yaml)
或 @yaml.register_class
之一
定义。这主要用于定义您自己的 classes
- 直接使用以下方式添加代表:
yaml.representer.add_representer(SomeClass, some_class_to_yaml)
前两种方式只是第三种方式的语法糖
方式,他们将尝试使用方法 to_yaml
和 class 属性
yaml_tag
如果可用,并尝试做一些明智的事情(如果有的话)
不可用。
您可以尝试 yaml.register(socket.AF_INET)
,但您会发现它失败了,因为:
AttributeError: 'AddressFamily' object has no attribute 'name'
所以你必须求助于第三种方式使用
add_representer()
。参数 some_class_to_yaml
是一个函数
那将是
当遇到 SomeClass
实例时调用,并调用该函数
使用 yaml.representer
实例作为第一个参数和实际数据
(SomeClass
的实例)作为第二个参数。
如果 SomeClass
是某种可以递归引用自身(间接)的容器类型,
你需要特别注意处理这种可能性,但对于 socket.AF_INET
来说,这是没有必要的。
具体的数据类型非常重要,您需要决定
如何 在 YAML 中表示类型。经常安静你会看到
SomeClass
的属性用作映射中的键(然后它
是获取标签的映射),但有时类型可以是
直接以 YAML 中可用的非集合类型表示,例如
作为字符串、整数等,对于其他 classes 更有意义
表示为(标记的)序列。
当您打印 type(socket.AF_INET)
时,您会注意到 "SomeClass" 实际上是 AddressFamily
。
在使用 dir()
检查 socket.AF_INET
之后,您会注意到有一个 name
属性,并且
很好地给你一个字符串 'AF_INET'
,可以用来告诉代表
如何将此数据表示为字符串,而不求助于某些查找:
import sys
import socket
import ruamel.yaml
def repr_socket(representer, data):
return representer.represent_scalar(u'!socket', data.name)
yaml = ruamel.yaml.YAML()
yaml.representer.add_representer(socket.AddressFamily, repr_socket)
data = dict(sock=socket.AF_INET)
yaml.dump(data, sys.stdout)
给出:
sock: !socket AF_INET
确保标签被定义为 unicode(如果您使用 Python 2.7,则必须如此)。
如果你也想加载这个,你可以用类似的方式扩展constructor
。但
这次你会得到一个 Node
,你需要将其转换为 AddressFamily
实例。
yaml_str = """\
- !socket AF_INET
- !socket AF_UNIX
"""
def constr_socket(constructor, node):
return getattr(socket, node.value)
yaml.constructor.add_constructor(u'!socket', constr_socket)
data = yaml.load(yaml_str)
assert data[0] == socket.AF_INET
assert data[1] == socket.AF_UNIX
它运行时没有抛出异常,并显示另一个
socket
中的常量也被处理。
我正在使用 python 库中的一个函数,它 returns 一个具有特定数据类型的对象。我想将该对象序列化为一个 yaml 文件,并且我想使用 ruamel.yaml。问题是 ruamel.yaml
不知道如何序列化函数 returns 并抛出异常的特定数据类型:
RepresenterError: cannot represent an object: <...>
问题是如何将数据类型 "declare" 改为 ruamel.yaml
以便它知道如何处理它。
注意:我不能/我不想对库或任何类似的内容进行更改。我只是 API.
的消费者为了使这个更具体,让我们使用以下使用 socket.AF_INET
which happens to be an IntEnum
的示例,但具体的数据类型应该不重要。
import sys
import socket
import ruamel.yaml
def third_party_lib():
""" Return a dict with our data """
return {"AF_INET": socket.AF_INET}
yaml = ruamel.yaml.YAML(typ="safe", pure=True)
yaml.dump(third_party_lib(), sys.stdout)
这给出了这个错误:
ruamel.yaml.YAML.dump(self, data, stream, **kw)
File "/home/feanor/Prog/git/vps-bench/.direnv/python-venv-3.7.2/lib/python3.7/site-packages/ruamel/yaml/main.py", line 439, in dump
return self.dump_all([data], stream, _kw, transform=transform)
File "/home/feanor/Prog/git/vps-bench/.direnv/python-venv-3.7.2/lib/python3.7/site-packages/ruamel/yaml/main.py", line 453, in dump_all
self._context_manager.dump(data)
File "/home/feanor/Prog/git/vps-bench/.direnv/python-venv-3.7.2/lib/python3.7/site-packages/ruamel/yaml/main.py", line 801, in dump
self._yaml.representer.represent(data)
File "/home/feanor/Prog/git/vps-bench/.direnv/python-venv-3.7.2/lib/python3.7/site-packages/ruamel/yaml/representer.py", line 84, in represent
node = self.represent_data(data)
File "/home/feanor/Prog/git/vps-bench/.direnv/python-venv-3.7.2/lib/python3.7/site-packages/ruamel/yaml/representer.py", line 111, in represent_data
node = self.yaml_representers[data_types[0]](self, data)
File "/home/feanor/Prog/git/vps-bench/.direnv/python-venv-3.7.2/lib/python3.7/site-packages/ruamel/yaml/representer.py", line 359, in represent_dict
return self.represent_mapping(u'tag:yaml.org,2002:map', data)
File "/home/feanor/Prog/git/vps-bench/.direnv/python-venv-3.7.2/lib/python3.7/site-packages/ruamel/yaml/representer.py", line 222, in represent_mapping
node_value = self.represent_data(item_value)
File "/home/feanor/Prog/git/vps-bench/.direnv/python-venv-3.7.2/lib/python3.7/site-packages/ruamel/yaml/representer.py", line 121, in represent_data
node = self.yaml_representers[None](self, data)
File "/home/feanor/Prog/git/vps-bench/.direnv/python-venv-3.7.2/lib/python3.7/site-packages/ruamel/yaml/representer.py", line 392, in represent_undefined
raise RepresenterError('cannot represent an object: %s' % data)
ruamel.yaml.representer.RepresenterError: cannot represent an object: AddressFamily.AF_INET
为了让ruamel.yaml
能够dump一个特定的class,不管你定义它,你得到它
从标准库或从其他地方获取,你需要 register that class against the representer。
(这在使用 YAML(typ='unsafe')
时不是必需的,但我假设您不想诉诸于此)。
可以通过不同的方式完成此注册。假设你有
完成yaml = ruamel.yaml.YAML()
或yaml = ruamel.yaml.YAML(typ='safe')
,并想代表SomeClass
,你可以:
- 使用
yaml.register_class(SomeClass)
。这可能适用于其他 classes,具体取决于 关于它们是如何定义的。 - 在
class SomeClass:
之前使用装饰器@yaml_object(yaml)
或@yaml.register_class
之一 定义。这主要用于定义您自己的 classes - 直接使用以下方式添加代表: yaml.representer.add_representer(SomeClass, some_class_to_yaml)
前两种方式只是第三种方式的语法糖
方式,他们将尝试使用方法 to_yaml
和 class 属性
yaml_tag
如果可用,并尝试做一些明智的事情(如果有的话)
不可用。
您可以尝试 yaml.register(socket.AF_INET)
,但您会发现它失败了,因为:
AttributeError: 'AddressFamily' object has no attribute 'name'
所以你必须求助于第三种方式使用
add_representer()
。参数 some_class_to_yaml
是一个函数
那将是
当遇到 SomeClass
实例时调用,并调用该函数
使用 yaml.representer
实例作为第一个参数和实际数据
(SomeClass
的实例)作为第二个参数。
如果 SomeClass
是某种可以递归引用自身(间接)的容器类型,
你需要特别注意处理这种可能性,但对于 socket.AF_INET
来说,这是没有必要的。
具体的数据类型非常重要,您需要决定
如何 在 YAML 中表示类型。经常安静你会看到
SomeClass
的属性用作映射中的键(然后它
是获取标签的映射),但有时类型可以是
直接以 YAML 中可用的非集合类型表示,例如
作为字符串、整数等,对于其他 classes 更有意义
表示为(标记的)序列。
当您打印 type(socket.AF_INET)
时,您会注意到 "SomeClass" 实际上是 AddressFamily
。
在使用 dir()
检查 socket.AF_INET
之后,您会注意到有一个 name
属性,并且
很好地给你一个字符串 'AF_INET'
,可以用来告诉代表
如何将此数据表示为字符串,而不求助于某些查找:
import sys
import socket
import ruamel.yaml
def repr_socket(representer, data):
return representer.represent_scalar(u'!socket', data.name)
yaml = ruamel.yaml.YAML()
yaml.representer.add_representer(socket.AddressFamily, repr_socket)
data = dict(sock=socket.AF_INET)
yaml.dump(data, sys.stdout)
给出:
sock: !socket AF_INET
确保标签被定义为 unicode(如果您使用 Python 2.7,则必须如此)。
如果你也想加载这个,你可以用类似的方式扩展constructor
。但
这次你会得到一个 Node
,你需要将其转换为 AddressFamily
实例。
yaml_str = """\
- !socket AF_INET
- !socket AF_UNIX
"""
def constr_socket(constructor, node):
return getattr(socket, node.value)
yaml.constructor.add_constructor(u'!socket', constr_socket)
data = yaml.load(yaml_str)
assert data[0] == socket.AF_INET
assert data[1] == socket.AF_UNIX
它运行时没有抛出异常,并显示另一个
socket
中的常量也被处理。