首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将数据类型声明为ruamel.yaml,以便它能够表示/序列化它?

将数据类型声明为ruamel.yaml,以便它能够表示/序列化它?
EN

Stack Overflow用户
提问于 2019-01-26 12:09:52
回答 1查看 3.5K关注 0票数 4

我正在使用python库中的一个函数,它返回具有特定数据类型的对象。我希望将该对象序列化为yaml文件,并且我希望使用ruamel.yaml。问题是,ruamel.yaml不知道如何序列化函数返回的特定数据类型并抛出异常:

代码语言:javascript
复制
RepresenterError: cannot represent an object: <...>

问题是如何将数据类型“声明”到ruamel.yaml,以便它知道如何处理它。

注意:我不能/我不想对库或任何类似的东西进行更改。我只是API的消费者。

为了更具体地说明这一点,让我们使用以下使用socket.AF_INET的示例,它碰巧是一个IntEnum,但是特定的数据类型不应该很重要。

代码语言:javascript
复制
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)

这就产生了这个错误:

代码语言:javascript
复制
    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
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-01-26 14:21:50

为了让ruamel.yaml能够转储一个特定的类,无论您是定义它,还是从标准库获得它,或者从其他地方获得它,您都需要根据代表注册这个类。(在使用YAML(typ='unsafe')时,这是不必要的,但我想您不想求助于此)。

这种注册可以用不同的方式进行。假设您已经完成了yaml = ruamel.yaml.YAML()yaml = ruamel.yaml.YAML(typ='safe'),并且希望表示SomeClass,则可以:

  • 使用yaml.register_class(SomeClass)。这可能对其他类起作用,具体取决于它们的定义方式。
  • 使用一个装饰器@yaml_object(yaml)@yaml.register_class,就在class SomeClass:定义之前。这主要是在定义您自己的类时使用的。
  • 直接使用: yaml.representer.add_representer(SomeClass,some_class_to_yaml)添加一个代表

前两种方法只是围绕第三种方法包装的语法糖,如果可用,它们将尝试使用方法to_yaml和类属性yaml_tag,如果两者都不可用,则尝试执行一些合理的操作。

您可以尝试yaml.register(socket.AF_INET),但是您会注意到它失败了,因为:

AttributeError:'AddressFamily‘对象没有属性'name

因此,您将不得不求助于使用add_representer()的第三种方式。参数some_class_to_yaml是在遇到SomeClass实例时将调用的函数,该函数是以yaml.representer实例作为第一个参数和以实际数据(SomeClass实例)作为第二个参数调用的。

如果SomeClass是某种可以(间接)递归引用自身的容器类型,则需要特别注意处理这种可能性,但是对于socket.AF_INET来说,这是不必要的。

到目前为止,特定的数据类型非常重要,因此您需要决定如何在YAML中表示该类型。您通常会看到,SomeClass的属性被用作映射中的键(然后是映射获取标记),但有时可以在YAML中可用的非集合类型(如字符串、int等)中直接表示该类型,对于其他类,将其表示为(标记的)序列更有意义。

当您打印type(socket.AF_INET)时,您会注意到"SomeClass“实际上是AddressFamily。在使用socket.AF_INET检查dir()之后,您将注意到有一个name属性,它很好地为您提供了一个字符串'AF_INET',它可以用于告诉代表如何将数据表示为字符串,而无需进行某些查找:

代码语言:javascript
复制
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)

这意味着:

代码语言:javascript
复制
sock: !socket AF_INET

确保标记被定义为unicode (在使用Python2.7时是必要的)。

如果您还想加载这个文件,您可以以类似的方式扩展constructor。但这一次您将得到一个需要转换为AddressFamily实例的AddressFamily

代码语言:javascript
复制
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中的其他常量也被处理。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54378220

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档