2010-04-13 3 views
6

Имея фрагмент кода, как это:Как десериализировать объект с помощью PyYAML с помощью safe_load?

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 с любыми данными, полученными из ненадежного источника; поэтому, что я должен изменить в свой класс snippet \ для использования метода safe_load?
Возможно ли это?

ответ

9

Похоже, что 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) 

Преимущество здесь в том, что у вас есть полный контроль над тем, как ваш класс (де) сериализации. Это означает, что вы не получите случайный исполняемый код по сети и запустите его. Недостатком является то, что у вас есть абсолютный контроль над тем, как ваш класс (de) сериализуется. Это означает, что вам нужно сделать гораздо больше работы. ;-)

+0

+1 Crystal clear.I видел __dict__ также для свалок с JSON. – systempuntoout

+0

Отлично! В основном вы просто сбрасываете пространство имен членов, и лучший способ сделать это - это dict. – Benson

16

Другой способ существует. Из документов 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) 

Преимущество этого является то, что это Prety легко сделать; недостатки заключаются в том, что он работает только с safe_load и загромождает ваш класс связанными с сериализацией атрибутами и метаклассом.

+0

хорошее решение. Спасибо – systempuntoout

+0

это решение намного чище. Ницца!!! –

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), чтобы завершить его.

 Смежные вопросы

  • Нет связанных вопросов^_^