我正在尝试使用JSON(使用simplejson)序列化python对象列表,并且得到对象“不是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的类型)。
使用simplejson将此序列化为json对象的最简单方法是什么?将其序列化为字典是否足够?简单地为ChildClass编写 dict 方法是最好的方法吗?最后,引用另一个对象的字段是否会使事情变得复杂化?如果是这样,我可以重写我的代码,只在类中有简单的字段(比如字符串/浮点数等)。
谢谢。答案 0 :(得分:27)
我过去曾经使用过这个策略,并对它非常满意:使用以下结构将自定义对象编码为JSON对象文字(如Python dict
):
{ '__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()
类方法,该方法知道如何从JSON解码的dict
重新创建实例。
扩展编码器和解码器以支持自定义类型(例如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}
}
}
}
答案 1 :(得分:8)
自定义类的实例可以在以下函数的帮助下表示为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's 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格式的字符串自定义类的实例
包含实例的字典 自定义类作为叶子,
答案 2 :(得分:2)
如果您使用的是Django,可以通过Django的序列化模块轻松完成。更多信息可以在这里找到:https://docs.djangoproject.com/en/dev/topics/serialization/
答案 3 :(得分:2)
在python的JSON文档中指定// 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)
答案 4 :(得分:1)
我有类似的问题,但我没有调用json.dump
函数。
因此,要使MyClass
JSON可序列化而不向json.dump
提供自定义编码器,您必须使用Monkey修补json编码器。
首先在模块my_module
中创建编码器:
import json
class JSONEncoder(json.JSONEncoder):
"""To make MyClass JSON serializable you have to Monkey patch the json
encoder with the following code:
>>> import json
>>> import my_module
>>> json.JSONEncoder.default = my_module.JSONEncoder.default
"""
def default(self, o):
"""For JSON serialization."""
if isinstance(o, MyClass):
return o.__repr__()
else:
return super(self,o)
class MyClass:
def __repr__(self):
return "my class representation"
然后正如评论中所描述的那样,猴子补丁json编码器:
import json
import my_module
json.JSONEncoder.default = my_module.JSONEncoder.default
现在,即使在外部库中调用json.dump
(您无法更改cls
参数)也适用于my_module.MyClass
个对象。
答案 5 :(得分:0)
我觉得我现在重新阅读它的2个解决方案有点傻了, 当然,当你使用django-rest-framework时,这个框架对于上面提到的这个问题有一些很好的特性。
在他们的网站上查看this model view example
如果你没有使用django-rest-framework,这无论如何都会有所帮助:
我在这个页面找到了2个有用的解决方案:(我最喜欢第二个!)
可能的解决方案1(或方式): David Chambers Design made a nice solution
我希望大卫不介意我复制粘贴他的解决方案代码:
在实例模型上定义序列化方法:
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上:
我希望他能看到这一点,我们可以谈谈在开放式解决方案中开始实施和改进他的代码吗?
答案 6 :(得分:0)
这是一种hackish,我确信它可能有很多错误。但是,我正在制作一个简单的脚本,我运行的问题是我不想将json序列化器子类化为序列化模型对象列表。我最终使用了列表理解
让: assets = modelobjects列表
代码:
myJson = json.dumps([x.__dict__ for x in assets])
到目前为止似乎已经为我的需求做了迷人的工作