【问题标题】:How to deserialize an object with PyYAML using safe_load?如何使用 safe_load 使用 PyYAML 反序列化对象?
【发布时间】:2010-04-13 06:52:41
【问题描述】:

有一个像这样的sn-p:

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 docs 表示使用从不受信任的来源收到的任何数据调用 yaml.load 是不安全的;那么,我应该如何修改我的 sn-p\class 以使用 safe_load 方法?
有可能吗?

【问题讨论】:

    标签: python deserialization pyyaml


    【解决方案1】:

    存在另一种方式。来自 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 并且用与序列化相关的属性和元类使你的类混乱。

    【讨论】:

      【解决方案2】:

      根据定义,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)
      

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

      【讨论】:

      • +1 非常清晰。我已经看到 dict 也用于 JSON 转储。
      • 太棒了!基本上你只是在转储成员命名空间,最好的方法是使用字典。
      【解决方案3】:

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

      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) 以完成它。

      【讨论】:

        猜你喜欢
        • 2014-05-11
        • 1970-01-01
        • 2019-06-03
        • 2019-03-09
        • 1970-01-01
        • 1970-01-01
        • 2021-12-08
        • 2012-06-22
        • 2019-10-25
        相关资源
        最近更新 更多