9

有这样的片段:

import yaml
class User(object):
    def __init__(self, name, surname):
       self.name= name
       self.surname= surname

user = User('spam', 'eggs')
serialized_user = yaml.dump(user)
#Network
deserialized_user = yaml.load(serialized_user)
print "name: %s, sname: %s" % (deserialized_user.name, deserialized_user.surname)

Yaml 文档说使用从不受信任的来源收到的任何数据调用yaml.load是不安全的;那么,我应该对我的片段\类进行什么修改以使用safe_load方法?
是否可以?

4

3 回答 3

26

存在另一种方式。来自 PyYaml 文档:

python 对象可以被标记为安全,因此被 yaml.safe_load 识别。为此,从 yaml.YAMLObject [...] 派生它并将其类属性 yaml_loader 显式设置为 yaml.SafeLoader。

您还必须设置 yaml_tag 属性才能使其工作。

YAMLObject 做了一些元类魔法来使对象可加载。请注意,如果您这样做,对象将只能由安全加载程序加载,而不是使用常规 yaml.load()。

工作示例:

import yaml

class User(yaml.YAMLObject):
    yaml_loader = yaml.SafeLoader
    yaml_tag = u'!User'

    def __init__(self, name, surname):
       self.name= name
       self.surname= surname

user = User('spam', 'eggs')
serialized_user = yaml.dump(user)

#Network

deserialized_user = yaml.safe_load(serialized_user)
print "name: %s, sname: %s" % (deserialized_user.name, deserialized_user.surname)

这个的优点是它很容易做到;缺点是它仅适用于 safe_load 并且使用与序列化相关的属性和元类使您的类混乱。

于 2010-05-22T23:00:41.710 回答
12

根据定义,safe_load 似乎不允许您反序列化自己的类。如果你想让它安全,我会做这样的事情:

import yaml
class User(object):
    def __init__(self, name, surname):
       self.name= name
       self.surname= surname

    def yaml(self):
       return yaml.dump(self.__dict__)

    @staticmethod
    def load(data):
       values = yaml.safe_load(data)
       return User(values["name"], values["surname"])

user = User('spam', 'eggs')
serialized_user = user.yaml()
print "serialized_user:  %s" % serialized_user.strip()

#Network
deserialized_user = User.load(serialized_user)
print "name: %s, sname: %s" % (deserialized_user.name, deserialized_user.surname)

这里的优点是您可以绝对控制您的类如何(反)序列化。这意味着您不会通过网络获得随机的可执行代码并运行它。缺点是您可以绝对控制您的类如何(反)序列化。这意味着你必须做更多的工作。;-)

于 2010-04-13T07:33:36.333 回答
2

如果您有许多标签并且不想为所有标签创建对象,或者如果您不关心返回的实际类型,只关心点访问,您可以使用以下代码捕获所有未定义的标签:

import yaml

class Blob(object):
    def update(self, kw):
        for k in kw:
            setattr(self, k, kw[k])

from yaml.constructor import SafeConstructor

def my_construct_undefined(self, node):
    data = Blob()
    yield data
    value = self.construct_mapping(node)
    data.update(value)

SafeConstructor.add_constructor(None, my_construct_undefined)


class User(object):
    def __init__(self, name, surname):
        self.name= name
        self.surname= surname

user = User('spam', 'eggs')
serialized_user = yaml.dump(user)
#Network
deserialized_user = yaml.safe_load(serialized_user)
print "name: %s, sname: %s" % (deserialized_user.name, deserialized_user.surname)

如果您想知道为什么中间my_construct_undefined有一个yield:它允许实例化对象与创建其子对象分开。一旦对象存在,它就可以被引用,以防它有一个锚点,并且孩子(或他们的孩子)有一个引用。创建对象的实际机制首先创建它,然后next(x)对其进行最终确定。

于 2016-02-23T14:39:03.707 回答