Самый простой способ сериализации простого объекта класса с simplejson?
Я пытаюсь сериализовать список объектов python с помощью JSON (используя simplejson), и я получаю ошибку, что объект "не является сериализуемым JSON".
Класс - это простой класс, имеющий поля, которые являются целыми числами, строками и поплавками, и наследует аналогичные поля из одного родительского суперкласса, например:
class ParentClass:
def __init__(self, foo):
self.foo = foo
class ChildClass(ParentClass):
def __init__(self, foo, bar):
ParentClass.__init__(self, foo)
self.bar = bar
bar1 = ChildClass(my_foo, my_bar)
bar2 = ChildClass(my_foo, my_bar)
my_list_of_objects = [bar1, bar2]
simplejson.dump(my_list_of_objects, my_filename)
где foo, bar - простые типы, как я упоминал выше. Единственное сложное дело в том, что ChildClass иногда имеет поле, которое ссылается на другой объект (типа, который не является ParentClass или ChildClass).
Какой простой способ сериализовать это как объект json с simplejson? Достаточно ли сделать его сериализуемым как словарь? Лучший способ просто написать метод dict для ChildClass? Наконец, имеет ли поле, которое относится к другому объекту, значительно усложняет ситуацию? Если это так, я могу переписать мой код, чтобы иметь только простые поля в классах (например, строки /float и т.д.).
спасибо.
Ответы
Ответ 1
Я использовал эту стратегию в прошлом и был очень доволен ею: кодируйте свои пользовательские объекты как литералы объектов JSON (например, Python dict
s) со следующей структурой:
{ '__ClassName__': { ... } }
Это, по сути, один элемент dict
, для которого единственным ключом является специальная строка, которая указывает, какой тип объекта кодируется, и значение которого является dict
атрибутов экземпляра. Если это имеет смысл.
Очень простая реализация кодировщика и декодера (упрощенная от используемого кода) выглядит так:
TYPES = { 'ParentClass': ParentClass,
'ChildClass': ChildClass }
class CustomTypeEncoder(json.JSONEncoder):
"""A custom JSONEncoder class that knows how to encode core custom
objects.
Custom objects are encoded as JSON object literals (ie, dicts) with
one key, '__TypeName__' where 'TypeName' is the actual name of the
type to which the object belongs. That single key maps to another
object literal which is just the __dict__ of the object encoded."""
def default(self, obj):
if isinstance(obj, TYPES.values()):
key = '__%s__' % obj.__class__.__name__
return { key: obj.__dict__ }
return json.JSONEncoder.default(self, obj)
def CustomTypeDecoder(dct):
if len(dct) == 1:
type_name, value = dct.items()[0]
type_name = type_name.strip('_')
if type_name in TYPES:
return TYPES[type_name].from_dict(value)
return dct
В этой реализации предполагается, что объекты, которые вы кодируете, будут иметь метод класса from_dict()
, который знает, как выполнить воссоздание экземпляра из dict
, декодированного из JSON.
Легко развернуть кодировщик и декодер для поддержки настраиваемых типов (например, datetime
объектов).
ИЗМЕНИТЬ, чтобы ответить на ваше редактирование. Хорошая вещь о реализации, подобной этой, заключается в том, что она будет автоматически кодировать и декодировать экземпляры любого объекта, найденного в сопоставлении TYPES
. Это означает, что он автоматически обработает ChildClass следующим образом:
class ChildClass(object):
def __init__(self):
self.foo = 'foo'
self.bar = 1.1
self.parent = ParentClass(1)
В результате JSON должен выглядеть примерно так:
{ '__ChildClass__': {
'bar': 1.1,
'foo': 'foo',
'parent': {
'__ParentClass__': {
'foo': 1}
}
}
}
Ответ 2
Экземпляр настраиваемого класса может быть представлен как форматированная строка JSON с помощью следующей функции:
def json_repr(obj):
"""Represent instance of a class as JSON.
Arguments:
obj -- any object
Return:
String that reprent JSON-encoded object.
"""
def serialize(obj):
"""Recursively walk object hierarchy."""
if isinstance(obj, (bool, int, long, float, basestring)):
return obj
elif isinstance(obj, dict):
obj = obj.copy()
for key in obj:
obj[key] = serialize(obj[key])
return obj
elif isinstance(obj, list):
return [serialize(item) for item in obj]
elif isinstance(obj, tuple):
return tuple(serialize([item for item in obj]))
elif hasattr(obj, '__dict__'):
return serialize(obj.__dict__)
else:
return repr(obj) # Don't know how to handle, convert to string
return json.dumps(serialize(obj))
Эта функция создаст строку в формате JSON для
-
экземпляр пользовательского класса,
-
словарь, который имеет экземпляры
пользовательские классы как листья,
- список экземпляров пользовательских
классы
Ответ 3
Если вы используете Django, его можно легко выполнить с помощью модуля Django serializers. Более подробную информацию можно найти здесь: https://docs.djangoproject.com/en/dev/topics/serialization/
Ответ 4
Как указано в python JSON docs// help(json.dumps)
//" >
Вы должны просто переопределить метод default()
JSONEncoder
, чтобы обеспечить преобразование пользовательского типа и передать его как аргумент cls
.
Вот один из них, который я использую для покрытия специальных типов данных Mongo (datetime и ObjectId)
class MongoEncoder(json.JSONEncoder):
def default(self, v):
types = {
'ObjectId': lambda v: str(v),
'datetime': lambda v: v.isoformat()
}
vtype = type(v).__name__
if vtype in types:
return types[type(v).__name__](v)
else:
return json.JSONEncoder.default(self, v)
Вызов так же просто, как
data = json.dumps(data, cls=MongoEncoder)
Ответ 5
Мне немного глупо о моих возможных 2 решениях, перечитывающих его сейчас,
конечно, когда вы используете django-rest-framework, эта структура имеет несколько отличных функций для этой проблемы, упомянутой выше.
см. этот пример представления модели на своем веб-сайте
Если вы не используете django-rest-framework, это может помочь в любом случае:
Я нашел 2 полезные решения для этой проблемы на этой странице: (Мне нравится второй самый!)
Возможное решение 1 (или способ перехода):
David Chambers Design сделал приятное решение
Надеюсь, Дэвид не против, я скопирую его код решения здесь:
Определите метод сериализации в модели экземпляра:
def toJSON(self):
import simplejson
return simplejson.dumps(dict([(attr, getattr(self, attr)) for attr in [f.name for f in self._meta.fields]]))
и он даже извлек метод выше, поэтому он более читабельен:
def toJSON(self):
fields = []
for field in self._meta.fields:
fields.append(field.name)
d = {}
for attr in fields:
d[attr] = getattr(self, attr)
import simplejson
return simplejson.dumps(d)
Пожалуйста, возражайте, это не мое решение, все кредиты идут по ссылке. Просто подумал, что это должно быть при переполнении стека.
Это также может быть реализовано в ответах выше.
Решение 2:
Мое предпочтительное решение можно найти на этой странице:
http://www.traddicts.org/webdevelopment/flexible-and-simple-json-serialization-for-django/
Кстати, я видел автора этого второго и лучшего решения: также на stackoverflow:
Selaux
Надеюсь, он это увидит, и мы можем поговорить о том, чтобы начать реализовывать и улучшать его код в открытом решении?
Ответ 6
Это своего рода хакерство, и я уверен, что, вероятно, это может быть неправильно. Тем не менее, я создавал простой script, и я запускал проблему, из-за которой я не хотел подклассифицировать сериализатор json для сериализации списка объектов модели. Я закончил использовать понимание списка
Пусть:
assets = список модельных объектов
код:
myJson = json.dumps([x.__dict__ for x in assets])
До сих пор, кажется, работало очаровательно для моих потребностей