如何使用 Python 读取自定义序列化 YAML 对象(由 Rails 编写)?

How do I read a custom serialized YAML object (written by Rails) with Python?

我正在使用 Rails 数据库,该数据库在一列中包含序列化值。这些值 应该 是常规的 Hashes,但由于对参数的不正确清理,它们被存储为 HashWithIndifferentAccessParameters。例如,一列条目如下所示:

--- !ruby/object:ActionController::Parameters
parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
  windowHeight: 946
  documentHeight: 3679
  scrollTop: 500
permitted: false

我想用 Python 的 yaml 实现来阅读这个,但是当我尝试这样做时,我得到:

*** yaml.constructor.ConstructorError: could not determine a constructor for the tag '!ruby/object:ActionController::Parameters'
  in "<unicode string>", line 1, column 5:
    --- !ruby/object:ActionController::P ...
        ^

所以,出于某种原因,它需要一个构造函数。但很明显,值本身只是一个普通字典。我怎么还能看呢?

您可以使用 PyYAML 解析器的 add_constructor(loader, node) 函数,它允许您为它无法识别的对象类型实现自定义构造函数。

在该构造函数中,可以调用函数loader.construct_pairs(node)从原始节点内容中获取键值元组。使用字典理解,我们可以创建原始字典。

由于条目是嵌套的,我们必须将构造函数应用于两种对象类型。

一个完整的例子如下:

import yaml

def convert_entry(loader, node):
    return { e[0]: e[1] for e in loader.construct_pairs(node) }

yaml.add_constructor('!ruby/hash:ActiveSupport::HashWithIndifferentAccess', convert_entry)
yaml.add_constructor('!ruby/object:ActionController::Parameters', convert_entry)

yaml.load(input_string)

这是somehow documented但是很难找到很多例子。