Python 如何使类JSON可序列化
如何使Python类可序列化 一个简单的类:Python 如何使类JSON可序列化,python,json,serialization,Python,Json,Serialization,如何使Python类可序列化 一个简单的类: class FileItem: def __init__(self, fname): self.fname = fname 我应该怎么做才能获得以下输出: >>> import json >>> my_file = FileItem('/foo/bar') >>> json.dumps(my_file) TypeError: Object of type 'FileIt
class FileItem:
def __init__(self, fname):
self.fname = fname
我应该怎么做才能获得以下输出:
>>> import json
>>> my_file = FileItem('/foo/bar')
>>> json.dumps(my_file)
TypeError: Object of type 'FileItem' is not JSON serializable
没有错误您对预期输出有什么想法?例如,这样做行吗
>>> f = FileItem("/foo/bar")
>>> magic(f)
'{"fname": "/foo/bar"}'
在这种情况下,您只能调用json.dumps(f.\uu dict\uu)
如果您想要更多定制的输出,那么您必须子类化并实现自己的定制序列化
有关一个简单的示例,请参见下文
>>> from json import JSONEncoder
>>> class MyEncoder(JSONEncoder):
def default(self, o):
return o.__dict__
>>> MyEncoder().encode(f)
'{"fname": "/foo/bar"}'
然后将该类作为cls
kwarg传递到方法中:
json.dumps(cls=MyEncoder)
如果您还想解码,那么您必须向类提供一个自定义的对象\u hook
。例如:
>>> def from_json(json_object):
if 'fname' in json_object:
return FileItem(json_object['fname'])
>>> f = JSONDecoder(object_hook = from_json).decode('{"fname": "/foo/bar"}')
>>> f
<__main__.FileItem object at 0x9337fac>
>>>
>>def from_json(json_对象):
如果json_对象中的“fname”:
返回FileItem(json_对象['fname'])
>>>f=JSONDecoder(object\u hook=from\u json).decode(“{”fname:“/foo/bar”}”)
>>>f
>>>
对于更复杂的类,您可以考虑工具:
jsonpickle是一个Python库,用于在JSON之间对复杂Python对象进行序列化和反序列化
用于将Python编码为JSON的标准Python库,如stdlib的JSON、simplejson和demjson,只能处理具有直接JSON等价物(例如dicts、list、string、int等)的Python原语。jsonpickle构建在这些库之上,允许将更复杂的数据结构序列化为JSON。jsonpickle具有高度可配置性和可扩展性,允许用户选择JSON后端并添加其他后端
另一个选项是在自己的类中包装JSON转储:
import json
class FileItem:
def __init__(self, fname):
self.fname = fname
def __repr__(self):
return json.dumps(self.__dict__)
或者,更好的是,从JsonSerializable
类中对FileItem类进行子类化:
import json
class JsonSerializable(object):
def toJson(self):
return json.dumps(self.__dict__)
def __repr__(self):
return self.toJson()
class FileItem(JsonSerializable):
def __init__(self, fname):
self.fname = fname
测试:
>>> f = FileItem('/foo/bar')
>>> f.toJson()
'{"fname": "/foo/bar"}'
>>> f
'{"fname": "/foo/bar"}'
>>> str(f) # string coercion
'{"fname": "/foo/bar"}'
以下是一个简单功能的简单解决方案:
.toJSON()
方法
实现序列化程序方法,而不是JSON可序列化类:
导入json
类对象:
def toJSON(self):
返回json.dumps(self,default=lambda o:o.。\uuuuu dict\uuuuu,
排序(关键字=True,缩进=4)
所以您只需调用它来序列化:
me=Object()
me.name=“Onur”
年龄=35
me.dog=Object()
me.dog.name=“阿波罗”
打印(me.toJSON())
将输出:
{
“年龄”:35岁,
“狗”:{
“名称”:“阿波罗”
},
“名称”:“Onur”
}
这是我的3美分…
这演示了类似于树的python对象的显式json序列化。
注意:如果你真的想要一些这样的代码,你可以使用这个类
jsonweb似乎是我最好的解决方案。看
我喜欢但会扩展到包含一个可选的toJSON()
方法,用于对象序列化自身:
def dumper(obj):
try:
return obj.toJSON()
except:
return obj.__dict__
print json.dumps(some_big_object, default=dumper, indent=2)
我想出了自己的解决办法。使用此方法,传递任何文档(dict、list、ObjectId等)进行序列化
def getSerializable(doc):
# check if it's a list
if isinstance(doc, list):
for i, val in enumerate(doc):
doc[i] = getSerializable(doc[i])
return doc
# check if it's a dict
if isinstance(doc, dict):
for key in doc.keys():
doc[key] = getSerializable(doc[key])
return doc
# Process ObjectId
if isinstance(doc, ObjectId):
doc = str(doc)
return doc
# Use any other custom serializting stuff here...
# For the rest of stuff
return doc
如果使用标准的json
,则需要定义一个default
函数
import json
def default(o):
return o._asdict()
print(json.dumps(User('alice', 'alice@mail.com'), default=default))
大多数答案都涉及将调用更改为json.dumps(),这并不总是可行或可取的(例如,它可能发生在框架组件内部)
如果您希望能够按原样调用json.dumps(obj),那么一个简单的解决方案就是继承dict:
class FileItem(dict):
def __init__(self, fname):
dict.__init__(self, fname=fname)
f = FileItem('tasks.txt')
json.dumps(f) #No need to change anything here
如果您的类只是基本的数据表示,那么这一点就行了,对于更复杂的事情,您总是可以显式地设置键。前几天我遇到了这个问题,并为Python对象实现了一个更通用的编码器版本,它可以处理嵌套对象和继承字段
class FileItem(dict):
def __init__(self, fname):
dict.__init__(self, fname=fname)
f = FileItem('tasks.txt')
json.dumps(f) #No need to change anything here
import json
class Foo(object):
def __init__(self):
self.bar = 'baz'
self._qux = 'flub'
def somemethod(self):
pass
def default(instance):
return {k: v
for k, v in vars(instance).items()
if not str(k).startswith('_')}
json_foo = json.dumps(Foo(), default=default)
assert '{"bar": "baz"}' == json_foo
print(json_foo)
导入json
进口检验
类ObjectEncoder(json.JSONEncoder):
def默认值(自身、obj):
如果hasattr(obj,“to_json”):
返回self.default(obj.to_json())
埃利夫·哈斯塔特(obj),“口述”:
d=听写(
(关键、价值)
对于键,inspect.getmembers(obj)中的值
如果不是键,则用(“\uuuuuuu”)启动
而不是检查。isabstract(值)
和不检查。isbuiltin(值)
和不检查。isfunction(值)
和不检查。isgenerator(值)
和不检查。isgeneratorfunction(值)
和不检查。ismethod(值)
和不检查。ismethoddescriptor(值)
和不检查。isroutine(值)
)
返回self.default(d)
返回obj
例如:
C类(对象):
c=“否”
def to_json(self):
返回{“c”:“是”}
B类(对象):
b=“b”
i=“i”
定义初始化(self,y):
self.y=y
def f(自我):
打印“f”
A类(B类):
a=“a”
定义初始化(自):
self.b=[{“ab”:b(“y”)}]
self.c=c()
打印json.dumps(A(),cls=ObjectEncoder,indent=2,sort\u keys=True)
结果:
{
“a”:“a”,
“b”:[
{
“ab”:{
“b”:“b”,
“我”:“我”,
“y”:“y”
}
}
],
“c”:{
“c”:“是”
},
“我”:“我”
}
json
在可以打印的对象方面受到限制,jsonpickle
(您可能需要pip安装jsonpickle
)在不能缩进文本方面受到限制。如果你想检查一个你不能更改其类别的对象的内容,我仍然找不到比以下更直接的方法:
import json
import jsonpickle
...
print json.dumps(json.loads(jsonpickle.encode(object)), indent=2)
注意:他们仍然无法打印对象方法 只需将添加到类的_json
方法,如下所示:
def to_json(self):
return self.message # or how you want it to be serialized
class SomeClass(Model):
json_field = JSONField()
class CustomJsonEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, SomeTypeUnsupportedByJsonDumps):
return < whatever value you want >
return json.JSONEncoder.default(self, obj)
@staticmethod
def json_dumper(obj):
return json.dumps(obj, cls=CustomJsonEncoder)
并将此代码(从)添加到所有内容的顶部:
from json import JSONEncoder
def _default(self, obj):
return getattr(obj.__class__, "to_json", _default.default)(obj)
_default.default = JSONEncoder().default
JSONEncoder.default = _default
这将在导入json模块时对其进行修补
default()自动检查特殊的“to_json()
方法,并使用它对找到的对象进行编码
正如Onur所说,但这次您不必更新项目中的每个json.dumps()
。这个类可以做到这一点,它将对象转换为标准json
import json
class Serializer(object):
@staticmethod
def serialize(object):
return json.dumps(object, default=lambda o: o.__dict__.values()[0])
用法:
Serializer.serialize(my_object)
在pytho中工作
pip install json-tricks
from json_tricks import dumps
json_str = dumps(cls_instance, indent=4)
{
"__instance_type__": [
"module_name.test_class",
"MyTestCls"
],
"attributes": {
"attr": "val",
"dct_attr": {
"hello": 42
}
}
}
from json_tricks import loads
json_str = loads(json_str)
class CustomEncodeCls:
def __init__(self):
self.relevant = 42
self.irrelevant = 37
def __json_encode__(self):
# should return primitive, serializable types like dict, list, int, string, float...
return {'relevant': self.relevant}
def __json_decode__(self, **attrs):
# should initialize all properties; note that __init__ is not called implicitly
self.relevant = attrs['relevant']
self.irrelevant = 12
class Serializer(object):
@staticmethod
def serialize(obj):
def check(o):
for k, v in o.__dict__.items():
try:
_ = json.dumps(v)
o.__dict__[k] = v
except TypeError:
o.__dict__[k] = str(v)
return o
return json.dumps(check(obj).__dict__, indent=2)
# Your custom class
class MyCustom(object):
def __json__(self):
return {
'a': self.a,
'b': self.b,
'__python__': 'mymodule.submodule:MyCustom.from_json',
}
to_json = __json__ # supported by simplejson
@classmethod
def from_json(cls, json):
obj = cls()
obj.a = json['a']
obj.b = json['b']
return obj
# Dumping and loading
import simplejson
obj = MyCustom()
obj.a = 3
obj.b = 4
json = simplejson.dumps(obj, for_json=True)
# Two-step loading
obj2_dict = simplejson.loads(json)
obj2 = MyCustom.from_json(obj2_dict)
# Make sure we have the correct thing
assert isinstance(obj2, MyCustom)
assert obj2.__dict__ == obj.__dict__
class SomeClass(Model):
json_field = JSONField()
class CustomJsonEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, SomeTypeUnsupportedByJsonDumps):
return < whatever value you want >
return json.JSONEncoder.default(self, obj)
@staticmethod
def json_dumper(obj):
return json.dumps(obj, cls=CustomJsonEncoder)
class SomeClass(Model):
json_field = JSONField(dumps=CustomJsonEncoder.json_dumper)
class TransactionType(Enum):
CURRENT = 1
STACKED = 2
def default(self, obj):
if isinstance(obj, TransactionType):
return obj.value
return json.JSONEncoder.default(self, obj)
peewee_model = WhateverPeeweeModel()
new_model = SomeClass()
new_model.json_field = model_to_dict(peewee_model)
pip install dill
# import pickle
import dill as pickle
import jsons
a_dict = jsons.dump(your_object)
a_str = jsons.dumps(your_object)
a_dict = your_object.json
class JsonSerializable(object):
def serialize(self):
return json.dumps(self.__dict__)
def __repr__(self):
return self.serialize()
@staticmethod
def dumper(obj):
if "serialize" in dir(obj):
return obj.serialize()
return obj.__dict__
class FileItem(JsonSerializable):
...
log.debug(json.dumps(<my object>, default=JsonSerializable.dumper, indent=2))
class ObjectEncoder(JSONEncoder):
def default(self, o):
return attr.asdict(o)
json.dumps(objects, cls=ObjectEncoder)
def from_json(o):
if '_obj_name' in o:
type_ = o['_obj_name']
del o['_obj_name']
return globals()[type_](**o)
else:
return o
data = JSONDecoder(object_hook=from_json).decode(data)
@attr.s
class Foo(object):
x = attr.ib()
_obj_name = attr.ib(init=False, default='Foo')
def serialize(o):
if isinstance(o, dict):
return {k:serialize(v) for k,v in o.items()}
if isinstance(o, list):
return [serialize(e) for e in o]
if isinstance(o, bytes):
return o.decode("utf-8")
return o
def sterilize(obj):
"""Make an object more ameniable to dumping as json
"""
if type(obj) in (str, float, int, bool, type(None)):
return obj
elif isinstance(obj, dict):
return {k: sterilize(v) for k, v in obj.items()}
list_ret = []
dict_ret = {}
for a in dir(obj):
if a == '__iter__' and callable(obj.__iter__):
list_ret.extend([sterilize(v) for v in obj])
elif a == '__dict__':
dict_ret.update({k: sterilize(v) for k, v in obj.__dict__.items() if k not in ['__module__', '__dict__', '__weakref__', '__doc__']})
elif a not in ['__doc__', '__module__']:
aval = getattr(obj, a)
if type(aval) in (str, float, int, bool, type(None)):
dict_ret[a] = aval
elif a != '__class__' and a != '__objclass__' and isinstance(aval, type):
dict_ret[a] = sterilize(aval)
if len(list_ret) == 0:
if len(dict_ret) == 0:
return repr(obj)
return dict_ret
else:
if len(dict_ret) == 0:
return list_ret
return (list_ret, dict_ret)
class DObject(json.JSONEncoder):
def delete_not_related_keys(self, _dict):
for key in ["skipkeys", "ensure_ascii", "check_circular", "allow_nan", "sort_keys", "indent"]:
try:
del _dict[key]
except:
continue
def default(self, o):
if hasattr(o, '__dict__'):
my_dict = o.__dict__.copy()
self.delete_not_related_keys(my_dict)
return my_dict
else:
return o
a = DObject()
a.name = 'abdul wahid'
b = DObject()
b.name = a
print(json.dumps(b, cls=DObject))
instance.toJSON()
instance.asJSON()
{
"members": {
"Flintstone,Fred": {
"firstName": "Fred",
"lastName": "Flintstone"
},
"Flintstone,Wilma": {
"firstName": "Wilma",
"lastName": "Flintstone"
}
},
"name": "The Flintstones"
}
{'name': 'The Flintstones', 'members': {'Flintstone,Fred': {'firstName': 'Fred', 'lastName': 'Flintstone'}, 'Flintstone,Wilma': {'firstName': 'Wilma', 'lastName': 'Flintstone'}}}
def testJsonAble(self):
family=Family("The Flintstones")
family.add(Person("Fred","Flintstone"))
family.add(Person("Wilma","Flintstone"))
json1=family.toJSON()
json2=family.asJSON()
print(json1)
print(json2)
class Family(JSONAble):
def __init__(self,name):
self.name=name
self.members={}
def add(self,person):
self.members[person.lastName+","+person.firstName]=person
class Person(JSONAble):
def __init__(self,firstName,lastName):
self.firstName=firstName;
self.lastName=lastName;
'''
Created on 2020-09-03
@author: wf
'''
import json
class JSONAble(object):
'''
mixin to allow classes to be JSON serializable see
https://stackoverflow.com/questions/3768895/how-to-make-a-class-json-serializable
'''
def __init__(self):
'''
Constructor
'''
def toJSON(self):
return json.dumps(self, default=lambda o: o.__dict__,
sort_keys=True, indent=4)
def getValue(self,v):
if (hasattr(v, "asJSON")):
return v.asJSON()
elif type(v) is dict:
return self.reprDict(v)
elif type(v) is list:
vlist=[]
for vitem in v:
vlist.append(self.getValue(vitem))
return vlist
else:
return v
def reprDict(self,srcDict):
'''
get my dict elements
'''
d = dict()
for a, v in srcDict.items():
d[a]=self.getValue(v)
return d
def asJSON(self):
'''
recursively return my dict elements
'''
return self.reprDict(self.__dict__)
json.dumps(obj, default=vars)