Как десериализовать объект с PyYAML, используя safe_load? - PullRequest
6 голосов
/ 13 апреля 2010

Имея такой фрагмент:

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

Ответы [ 3 ]

21 голосов
/ 23 мая 2010

Другой способ существует. Из документов 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 и загромождает ваш класс связанными с сериализацией атрибутами и метаклассом.

10 голосов
/ 13 апреля 2010

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

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

2 голосов
/ 23 февраля 2016

Если у вас много тегов и вы не хотите создавать объекты для всех них, или если вам не нужен фактический возвращаемый тип, а только доступ с точками, вы перехватываете все неопределенные теги с помощью следующего кода:

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) для его завершения.

...