如何使类 JSON 可序列化
- 2024-11-20 08:44:00
- admin 原创
- 9
问题描述:
如何使 Python 类可序列化?
class FileItem:
def __init__(self, fname):
self.fname = fname
尝试序列化为 JSON:
>>> import json
>>> x = FileItem('/foo/bar')
>>> json.dumps(x)
TypeError: Object of type 'FileItem' is not JSON serializable
解决方案 1:
这是一个针对简单功能的简单解决方案:
.toJSON()
方法
不要使用 JSON 可序列化类,而是实现序列化器方法:
import json
class Object:
def toJSON(self):
return json.dumps(
self,
default=lambda o: o.__dict__,
sort_keys=True,
indent=4)
因此你只需调用它来序列化:
me = Object()
me.name = "Onur"
me.age = 35
me.dog = Object()
me.dog.name = "Apollo"
print(me.toJSON())
将输出:
{
"age": 35,
"dog": {
"name": "Apollo"
},
"name": "Onur"
}
对于功能齐全的库,您可以使用orjson。
解决方案 2:
您对预期输出有什么想法吗?例如,这样可以吗?
>>> f = FileItem("/foo/bar")
>>> magic(f)
'{"fname": "/foo/bar"}'
在这种情况下您只需拨打电话即可json.dumps(f.__dict__)
。
如果您想要更多定制的输出,那么您将必须子类化JSONEncoder
并实现您自己的自定义序列化。
请参阅下文作为一个简单的例子。
>>> from json import JSONEncoder
>>> class MyEncoder(JSONEncoder):
def default(self, o):
return o.__dict__
>>> MyEncoder().encode(f)
'{"fname": "/foo/bar"}'
然后将此类json.dumps()
作为cls
kwarg 传递到方法中:
json.dumps(cls=MyEncoder)
如果您还想解码,那么您必须object_hook
为该类提供自定义JSONDecoder
。例如:
>>> 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>
>>>
解决方案 3:
对于更复杂的类,你可以考虑使用工具jsonpickle:
jsonpickle 是一个 Python 库,用于将复杂的 Python 对象序列化为 JSON 或从 JSON 反序列化。
用于将 Python 编码为 JSON 的标准 Python 库(例如 stdlib 的 json、simplejson 和 demjson)只能处理具有直接 JSON 等效项的 Python 原语(例如字典、列表、字符串、整数等)。jsonpickle 建立在这些库之上,并允许将更复杂的数据结构序列化为 JSON。jsonpickle 具有高度可配置性和可扩展性 - 允许用户选择 JSON 后端并添加其他后端。
将对象转换为 JSON 字符串:
import jsonpickle
json_string = jsonpickle.encode(obj)
从 JSON 字符串重新创建 Python 对象:
recreated_obj = jsonpickle.decode(json_string)
(PyPi 上的 jsonpickle 链接)
解决方案 4:
大多数答案都涉及将调用更改为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
如果您的类只是基本的数据表示,那么这种方法是可行的,对于比较棘手的事情,您总是可以在调用时明确设置键dict.__init__()
。
之所以有效是因为它json.dumps()
通过一种相当非 Python 的方式检查对象是否是几种已知类型之一- 所以如果你真的不想从继承的话,isinstance(value, dict)
就可以用这种方法和其他一些方法来伪造它。__class__
`dict`
解决方案 5:
正如许多其他答案中提到的那样,你可以传递一个函数来json.dumps
将默认不支持的类型的对象转换为支持的类型。令人惊讶的是,他们都没有提到最简单的情况,即使用内置函数vars
将对象转换为包含其所有属性的字典:
json.dumps(obj, default=vars)
请注意,这仅涵盖基本情况,如果您需要对某些类型进行更具体的序列化(例如,排除某些属性或没有属性的对象__dict__
),则需要使用自定义函数或JSONEncoder
其他答案中所述。
解决方案 6:
只需to_json
像这样向您的课程添加方法:
def to_json(self):
return self.message # or how you want it to be serialized
并将此代码(来自此答案)添加到所有内容顶部的某处:
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 模块进行 monkey-patch,因此会JSONEncoder.default()
自动检查特殊to_json()
方法,并在找到后使用它对对象进行编码。
就像 Onur 所说的那样,但这次您不必更新json.dumps()
项目中的每一个。
解决方案 7:
TLDR:复制粘贴下面的选项 1 或选项 2
所以你想让 Python 的json
模块与你的类一起工作吗?
好消息:是的,有一个可靠的解决方案
坏消息:没有 Python“官方”解决方案
官方解决方案是指,截至 2024 年,没有办法(如
toJSON
JavaScript 中那样)向您的类添加方法,并且/或者没有办法使用内置 json 模块注册您的类。json.dumps([1,2, your_obj])
执行类似操作时,python 不会检查查找表或对象方法。我不确定为什么其他答案没有解释这一点
最接近的官方方法可能是andyhasit 的答案,即从字典继承。但是,对于许多自定义类(如 AdvancedDateTime 或 pytorch 张量),从字典继承效果不佳。
理想的解决方法是这样的:
def __json__(self)
向您的班级添加方法变异
json.dumps
以检查__json__
方法(影响所有地方,甚至影响导入 json 的 pip 模块)注意:修改内置内容通常不是很好,但是这种更改应该没有副作用,即使它被不同的代码库多次应用。它在运行时是完全可逆的(如果模块想要撤消修改)。无论好坏,这是目前能做的最好的事情。
选项 1:让模块进行修补
pip install json-fix
( Fancy John 答案
的扩展+打包版本,感谢@FancyJohn)
your_class_definition.py
import json_fix
class YOUR_CLASS:
def __json__(self):
# YOUR CUSTOM CODE HERE
# you probably just want to do:
# return self.__dict__
return "a built-in object that is naturally json-able"
就是这样。
使用示例:
from your_class_definition import YOUR_CLASS
import json
json.dumps([1,2, YOUR_CLASS()], indent=0)
# '[
1,
2,
"a built-in object that is naturally json-able"
]'
为了使json.dumps
Numpy 数组、Pandas DataFrames 和其他第三方对象能够工作,请参阅模块(只有约 2 行代码,但需要解释)。
它是如何工作的?嗯...
选项 2:自己修补 json.dumps
注意:这种方法是简化的,它在已知的边缘情况(例如:如果你的自定义类继承自dict
或另一个内置类)上会失败,并且它错过了控制外部类(numpy 数组、日期时间、数据框、张量等)的 json 行为。
some_file_thats_imported_before_your_class_definitions.py
# Step: 1
# create the patch
from json import JSONEncoder
def wrapped_default(self, obj):
return getattr(obj.__class__, "__json__", wrapped_default.default)(obj)
wrapped_default.default = JSONEncoder().default
# apply the patch
JSONEncoder.original_default = JSONEncoder.default
JSONEncoder.default = wrapped_default
你的类定义.py
# Step 2
class YOUR_CLASS:
def __json__(self, **options):
# YOUR CUSTOM CODE HERE
# you probably just want to do:
# return self.__dict__
return "a built-in object that is natually json-able"
_
所有其他答案似乎都是“序列化自定义对象的最佳实践/方法”
这已经在文档中介绍过了(搜索“complex”可以找到编码复数的示例)
解决方案 8:
我喜欢Onur 的答案,但会扩展以包含一个toJSON()
用于对象自身序列化的可选方法:
def dumper(obj):
try:
return obj.toJSON()
except:
return obj.__dict__
print json.dumps(some_big_object, default=dumper, indent=2)
解决方案 9:
如果您使用的是 Python3.5+,则可以使用jsons
。(PyPi: https: //pypi.org/project/jsons/)它会将您的对象(及其所有属性递归地)转换为字典。
import jsons
a_dict = jsons.dump(your_object)
或者如果你想要一个字符串:
a_str = jsons.dumps(your_object)
或者如果你的班级实现了jsons.JsonSerializable
:
a_dict = your_object.json
解决方案 10:
另一种选择是将 JSON 转储包装在其自己的类中:
import json
class FileItem:
def __init__(self, fname: str) -> None:
self.fname = fname
def __repr__(self) -> str:
return json.dumps(self.__dict__)
或者,更好的是,从JsonSerializable
协议类中子类化 FileItem 类:
import json
from typing import Protocol
class JsonSerializable(Protocol):
def to_json(self) -> str:
return json.dumps(self.__dict__)
def __repr__(self) -> str:
return self.to_json()
class FileItem(JsonSerializable):
def __init__(self, fname: str) -> None:
self.fname = fname
测试:
>>> f = FileItem('/foo/bar')
>>> f.to_json()
'{"fname": "/foo/bar"}'
>>> f
'{"fname": "/foo/bar"}'
>>> str(f) # string coercion
'{"fname": "/foo/bar"}'
解决方案 11:
前几天我遇到了这个问题,并实现了一个更通用的 Python 对象编码器版本,可以处理嵌套对象和继承字段:
import json
import inspect
class ObjectEncoder(json.JSONEncoder):
def default(self, obj):
if hasattr(obj, "to_json"):
return self.default(obj.to_json())
elif hasattr(obj, "__dict__"):
d = dict(
(key, value)
for key, value in inspect.getmembers(obj)
if not key.startswith("__")
and not inspect.isabstract(value)
and not inspect.isbuiltin(value)
and not inspect.isfunction(value)
and not inspect.isgenerator(value)
and not inspect.isgeneratorfunction(value)
and not inspect.ismethod(value)
and not inspect.ismethoddescriptor(value)
and not inspect.isroutine(value)
)
return self.default(d)
return obj
例子:
class C(object):
c = "NO"
def to_json(self):
return {"c": "YES"}
class B(object):
b = "B"
i = "I"
def __init__(self, y):
self.y = y
def f(self):
print "f"
class A(B):
a = "A"
def __init__(self):
self.b = [{"ab": B("y")}]
self.c = C()
print json.dumps(A(), cls=ObjectEncoder, indent=2, sort_keys=True)
结果:
{
"a": "A",
"b": [
{
"ab": {
"b": "B",
"i": "I",
"y": "y"
}
}
],
"c": {
"c": "YES"
},
"i": "I"
}
解决方案 12:
一个非常简单的单行解决方案
import json
json.dumps(your_object, default=vars)
完结!
下面是一个测试。
import json
from dataclasses import dataclass
@dataclass
class Company:
id: int
name: str
@dataclass
class User:
id: int
name: str
email: str
company: Company
company = Company(id=1, name="Example Ltd")
user = User(id=1, name="John Doe", email="john@doe.net", company=company)
json.dumps(user, default=vars)
输出:
{
"id": 1,
"name": "John Doe",
"email": "john@doe.net",
"company": {
"id": 1,
"name": "Example Ltd"
}
}
解决方案 13:
import simplejson
class User(object):
def __init__(self, name, mail):
self.name = name
self.mail = mail
def _asdict(self):
return self.__dict__
print(simplejson.dumps(User('alice', 'alice@mail.com')))
如果使用标准json
,则需要定义一个default
函数
import json
def default(o):
return o._asdict()
print(json.dumps(User('alice', 'alice@mail.com'), default=default))
解决方案 14:
最简单的答案
class Object(dict):
def __init__(self):
pass
def __getattr__(self, key):
return self[key]
def __setattr__(self, key, value):
self[key] = value
# test
obj = Object()
obj.name = "John"
obj.age = 25
obj.brothers = [ Object() ]
text = json.dumps(obj)
现在它会给你输出,不要将任何内容更改为 json.dumps(...)
'{"name": "John", "age": 25, "brothers": [{}]}'
解决方案 15:
json
在可以打印的对象方面受到限制,并且jsonpickle
(您可能需要pip install jsonpickle
)在无法缩进文本方面受到限制。如果您想检查无法更改其类的对象的内容,我仍然找不到比以下更直接的方法:
import json
import jsonpickle
...
print json.dumps(json.loads(jsonpickle.encode(object)), indent=2)
注意:他们仍然无法打印对象方法。
解决方案 16:
jaraco给出了一个非常简洁的答案。我需要修复一些小问题,但这有效:
代码
# 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__
注意,加载需要两个步骤。目前,该__python__
属性尚未使用。
这种现象有多常见?
使用AlJohri的方法,我检查了方法的流行度:
序列化(Python -> JSON):
to_json
:2018-06-27 共 266,595 人toJSON
: 2018-06-27 共 96,307 人__json__
:2018-06-27 上 8,504for_json
: 2018-06-27 共 6,937 人
反序列化(JSON -> Python):
from_json
: 2018-06-27 共 226,101 人
解决方案 17:
为了给这场持续了 11 年的大火再添一笔,我想要一个满足以下标准的解决方案:
允许仅使用以下方法序列化 FileItem 类的实例
json.dumps(obj)
允许 FileItem 实例具有属性:fileItem.fname
允许将 FileItem 实例提供给任何可以使用以下方法序列化的库
json.dumps(obj)
不需要传递任何其他字段
json.dumps
(例如自定义序列化器)
IE:
fileItem = FileItem('filename.ext')
assert json.dumps(fileItem) == '{"fname": "filename.ext"}'
assert fileItem.fname == 'filename.ext'
我的解决方案是:
让 obj 的类继承自
dict
将每个对象属性映射到底层
dict
class FileItem(dict):
def __init__(self, fname):
self['fname'] = fname
#fname property
fname: str = property()
@fname.getter
def fname(self):
return self['fname']
@fname.setter
def fname(self, value: str):
self['fname'] = value
#Repeat for other properties
是的,如果您有很多属性,这会有点冗长,但它是 JSONSerializable,它的行为就像一个对象,您可以将它提供给任何需要json.dumps(obj)
它的库。
解决方案 18:
这是我的 3 分...
这演示了树状 python 对象的显式 json 序列化。
注意:如果您确实想要这样的代码,则可以使用twisted FilePath类。
import json, sys, os
class File:
def __init__(self, path):
self.path = path
def isdir(self):
return os.path.isdir(self.path)
def isfile(self):
return os.path.isfile(self.path)
def children(self):
return [File(os.path.join(self.path, f))
for f in os.listdir(self.path)]
def getsize(self):
return os.path.getsize(self.path)
def getModificationTime(self):
return os.path.getmtime(self.path)
def _default(o):
d = {}
d['path'] = o.path
d['isFile'] = o.isfile()
d['isDir'] = o.isdir()
d['mtime'] = int(o.getModificationTime())
d['size'] = o.getsize() if o.isfile() else 0
if o.isdir(): d['children'] = o.children()
return d
folder = os.path.abspath('.')
json.dump(File(folder), sys.stdout, default=_default)
解决方案 19:
这个类可以达到这个目的,它将对象转换为标准 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)
python2.7
在和工作python3
。
解决方案 20:
你们为什么要搞得这么复杂?这是一个简单的例子:
#!/usr/bin/env python3
import json
from dataclasses import dataclass
@dataclass
class Person:
first: str
last: str
age: int
@property
def __json__(self):
return {
"name": f"{self.first} {self.last}",
"age": self.age
}
john = Person("John", "Doe", 42)
print(json.dumps(john, indent=4, default=lambda x: x.__json__))
这样,您还可以序列化嵌套类,因为__json__
返回的是 Python 对象而不是字符串。无需使用JSONEncoder
,因为default
带有简单 lambda 的参数也可以正常工作。
我使用了@property
简单的函数,因为这样感觉更自然、更现代。这@dataclass
只是一个例子,它也适用于“普通”类。
解决方案 21:
这对我来说很有效:
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))
解决方案 22:
import json
class Foo(object):
def __init__(self):
self.bar = 'baz'
self._qux = 'flub'
def somemethod(self):
pass
'''
The parameter default(obj) is a function that should return a
serializable version of obj or raise TypeError. The default
default simply raises TypeError.
https://docs.python.org/3.4/library/json.html#json.dumps
'''
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)
解决方案 23:
如果您不介意为其安装包,则可以使用json-tricks:
pip install json-tricks
之后,您只需要从 json 中导入dump(s)
,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
}
}
}
基本上就是这样!
总体来说,这很有效。但也有一些例外,例如,如果发生了特殊情况__new__
,或者发生了更多元类魔法。
显然加载也有效(否则还有什么意义):
from json_tricks import loads
json_str = loads(json_str)
这确实假设module_name.test_class.MyTestCls
可以导入并且没有以不兼容的方式更改。您将返回一个实例,而不是一些字典或类似的东西,并且它应该是与您转储的那个相同的副本。
如果你想要自定义某些东西如何被序列化(反序列化),你可以向你的类中添加特殊方法,就像这样:
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
作为示例,它仅序列化部分属性参数。
作为免费奖励,您可以获得 numpy 数组、日期和时间、有序映射的序列化(反序列化),以及在 json 中包含注释的能力。
免责声明:我创建了json_tricks,因为我遇到了与您相同的问题。
解决方案 24:
Kyle Delaney 的评论是正确的,所以我尝试使用答案https://stackoverflow.com/a/15538391/1497139以及https://stackoverflow.com/a/10254820/1497139的改进版本
创建一个“JSONAble”混合类。
因此,要使类 JSON 可序列化,请使用“JSONAble”作为超类,然后调用:
instance.toJSON()
或者
instance.asJSON()
对于提供的两种方法。您还可以使用此处提供的其他方法扩展 JSONAble 类。
包含家庭和人员样本的单元测试示例结果如下:
到JSOn():
{
"members": {
"Flintstone,Fred": {
"firstName": "Fred",
"lastName": "Flintstone"
},
"Flintstone,Wilma": {
"firstName": "Wilma",
"lastName": "Flintstone"
}
},
"name": "The Flintstones"
}
asJSOn():
{'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;
jsonable.py 定义 JSONAble mixin
'''
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__)
您会发现这些方法现已集成在https://github.com/WolfgangFahl/pyLoDStorage项目中,该项目可在https://pypi.org/project/pylodstorage/上找到
解决方案 25:
jsonweb 似乎是我的最佳解决方案。请参阅http://www.jsonweb.info/en/latest/
from jsonweb.encode import to_object, dumper
@to_object()
class DataModel(object):
def __init__(self, id, value):
self.id = id
self.value = value
>>> data = DataModel(5, "foo")
>>> dumper(data)
'{"__type__": "DataModel", "id": 5, "value": "foo"}'
解决方案 26:
我想到了自己的解决方案。使用此方法,传递任何文档(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
解决方案 27:
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))
解决方案 28:
基于Quinten Cabo的回答:
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)
不同之处在于
适用于任何可迭代对象,而不仅仅是
list
(tuple
它适用于 NumPy 数组等)适用于动态类型(包含 的类型
__dict__
)。包括本机类型
float
,None
因此它们不会转换为字符串。__dict__
具有和成员的类大部分都可以工作(如果__dict__
和成员名称冲突,则只会得到一个 - 可能是成员)具有成员的列表类看起来像列表和字典的元组
Python3(该
isinstance()
调用可能是唯一需要更改的内容)
解决方案 29:
我最喜欢 Lost Koder 的方法。当我尝试序列化成员/方法不可序列化的更复杂对象时,我遇到了问题。这是我对更多对象有效的实现:
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)
解决方案 30:
当我尝试将 Peewee 的模型存储到 PostgreSQL 中时遇到了这个问题JSONField
。
经过一段时间的努力,这里是通用的解决方案。
我的解决方案的关键是查看 Python 的源代码并意识到代码文档(此处描述)已经解释了如何扩展现有代码json.dumps
以支持其他数据类型。
假设您当前有一个模型,其中包含一些不能序列化为 JSON 的字段,并且包含 JSON 字段的模型最初如下所示:
class SomeClass(Model):
json_field = JSONField()
只需定义JSONEncoder
这样的自定义:
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)
然后像JSONField
下面这样使用它:
class SomeClass(Model):
json_field = JSONField(dumps=CustomJsonEncoder.json_dumper)
关键是default(self, obj)
上面的方法。对于你收到的来自 Python 的每一个... is not JSON serializable
投诉,只需添加代码来处理不可序列化为 JSON 的类型(例如Enum
或datetime
)
例如,下面是我支持从 继承的类的方法Enum
:
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 模型转换为 JSON 可序列化对象,如下所示:
peewee_model = WhateverPeeweeModel()
new_model = SomeClass()
new_model.json_field = model_to_dict(peewee_model)
虽然上面的代码(在某种程度上)特定于 Peewee,但我认为:
它通常适用于其他 ORM(Django 等)
另外,如果你理解了它
json.dumps
的工作原理,这个解决方案也适用于 Python(无 ORM)
如有任何问题,请在评论部分发表。谢谢!
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件