我正在尝试创建类实例的JSON字符串表示并且有困难。假设这个类是这样构建的:
class testclass:
value1 = "a"
value2 = "b"
对json.dumps的调用是这样的:
t = testclass()
json.dumps(t)
失败告诉我,测试类不是JSON可序列化的。
TypeError: <__main__.testclass object at 0x000000000227A400> is not JSON serializable
我也尝试过使用pickle模块:
t = testclass()
print(pickle.dumps(t, pickle.HIGHEST_PROTOCOL))
它提供类实例信息,但不提供类实例的序列化内容。
b'\x80\x03c__main__\ntestclass\nq\x00)\x81q\x01}q\x02b.'
我做错了什么?
答案 0 :(得分:192)
基本问题是JSON编码器json.dumps()
只知道如何序列化一组有限的对象类型,默认情况下是所有内置类型。在此列出:https://docs.python.org/3.3/library/json.html#encoders-and-decoders
一个好的解决方案是让你的类继承自JSONEncoder
,然后实现JSONEncoder.default()
函数,并使该函数为你的类发出正确的JSON。
一个简单的解决方案是在该实例的json.dumps()
成员上调用.__dict__
。这是一个标准的Python dict
,如果你的类很简单,它将是JSON可序列化的。
class Foo(object):
def __init__(self):
self.x = 1
self.y = 2
foo = Foo()
s = json.dumps(foo) # raises TypeError with "is not JSON serializable"
s = json.dumps(foo.__dict__) # s set to: {"x":1, "y":2}
上述方法在本博文中讨论:
答案 1 :(得分:42)
有一种方法对我很有用,你可以尝试:
json.dumps()
可以使用可选参数默认,您可以在其中为未知类型指定自定义序列化程序函数,在我的示例中看起来像
def serialize(obj):
"""JSON serializer for objects not serializable by default json code"""
if isinstance(obj, date):
serial = obj.isoformat()
return serial
if isinstance(obj, time):
serial = obj.isoformat()
return serial
return obj.__dict__
前两个ifs用于日期和时间序列化
然后为任何其他对象返回obj.__dict__
。
最终通话如下:
json.dumps(myObj, default=serialize)
当您序列化一个集合并且不想为每个对象显式调用__dict__
时,它会特别好。这是为你自动完成的。
到目前为止,对我来说工作非常好,期待你的想法。
答案 2 :(得分:33)
您可以在default
函数中指定json.dumps()
命名参数:
json.dumps(obj, default=lambda x: x.__dict__)
说明:
``default(obj)`` is a function that should return a serializable version
of obj or raise TypeError. The default simply raises TypeError.
(适用于Python 2.7和Python 3.x)
注意:在这种情况下,您需要instance
个变量而不是class
个变量,因为问题中的示例会尝试这样做。 (我假设提问者意味着class instance
是一个类的对象)
我首先从@ phihag的回答here中学到了这一点。发现它是最简单,最干净的工作方式。
答案 3 :(得分:22)
我只是这样做:
data=json.dumps(myobject.__dict__)
这不是完整的答案,如果你有某种复杂的对象类,你肯定不会得到所有东西。但是我将它用于我的一些简单对象。
它可以很好地工作的是从OptionParser模块获得的“选项”类。 这里是JSON请求本身。
def executeJson(self, url, options):
data=json.dumps(options.__dict__)
if options.verbose:
print data
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
return requests.post(url, data, headers=headers)
答案 4 :(得分:12)
import jsonpickle
object = YourClass()
json_object = jsonpickle.encode(object)
答案 5 :(得分:4)
以下是两个用于序列化任何非复杂类的简单函数,如前所述,没什么特别的。
我将它用于配置类型的东西,因为我可以在没有代码调整的情况下向类中添加新成员。
import json
class SimpleClass:
def __init__(self, a=None, b=None, c=None):
self.a = a
self.b = b
self.c = c
def serialize_json(instance=None, path=None):
dt = {}
dt.update(vars(instance))
with open(path, "w") as file:
json.dump(dt, file)
def deserialize_json(cls=None, path=None):
def read_json(_path):
with open(_path, "r") as file:
return json.load(file)
data = read_json(path)
instance = object.__new__(cls)
for key, value in data.items():
setattr(instance, key, value)
return instance
# Usage: Create class and serialize under Windows file system.
write_settings = SimpleClass(a=1, b=2, c=3)
serialize_json(write_settings, r"c:\temp\test.json")
# Read back and rehydrate.
read_settings = deserialize_json(SimpleClass, r"c:\temp\test.json")
# results are the same.
print(vars(write_settings))
print(vars(read_settings))
# output:
# {'c': 3, 'b': 2, 'a': 1}
# {'c': 3, 'b': 2, 'a': 1}
答案 6 :(得分:3)
JSON并不是用于序列化任意Python对象。它非常适合序列化dict
个对象,但pickle
模块实际上是您应该使用的模块。来自pickle
的输出实际上并不是人类可读的,但它应该是非常好的。如果您坚持使用JSON,可以查看jsonpickle
模块,这是一种有趣的混合方法。
答案 7 :(得分:1)
我认为不是接受答案中建议的继承,而是使用多态。否则,您必须有一个大的if else语句来自定义每个对象的编码。这意味着为JSON创建一个通用的默认编码器:
def jsonDefEncoder(obj):
if hasattr(obj, 'jsonEnc'):
return obj.jsonEnc()
else: #some default behavior
return obj.__dict__
然后在要序列化的每个类中都有一个jsonEnc()
函数。 e.g。
class A(object):
def __init__(self,lengthInFeet):
self.lengthInFeet=lengthInFeet
def jsonEnc(self):
return {'lengthInMeters': lengthInFeet * 0.3 } # each foot is 0.3 meter
然后你拨打json.dumps(classInstance,default=jsonDefEncoder)
答案 8 :(得分:1)
Python3.x
我所能达到的最好的方法就是这个。
请注意,这段代码也会处理set()。
这种方法是通用的,只需要扩展类(在第二个示例中)。
请注意,我只是在处理文件,但是很容易根据自己的喜好修改行为。
但这是CoDec。
通过更多的工作,您可以用其他方式构造您的课程。 我假定使用默认的构造函数来实例化它,然后更新类dict。
import json
import collections
class JsonClassSerializable(json.JSONEncoder):
REGISTERED_CLASS = {}
def register(ctype):
JsonClassSerializable.REGISTERED_CLASS[ctype.__name__] = ctype
def default(self, obj):
if isinstance(obj, collections.Set):
return dict(_set_object=list(obj))
if isinstance(obj, JsonClassSerializable):
jclass = {}
jclass["name"] = type(obj).__name__
jclass["dict"] = obj.__dict__
return dict(_class_object=jclass)
else:
return json.JSONEncoder.default(self, obj)
def json_to_class(self, dct):
if '_set_object' in dct:
return set(dct['_set_object'])
elif '_class_object' in dct:
cclass = dct['_class_object']
cclass_name = cclass["name"]
if cclass_name not in self.REGISTERED_CLASS:
raise RuntimeError(
"Class {} not registered in JSON Parser"
.format(cclass["name"])
)
instance = self.REGISTERED_CLASS[cclass_name]()
instance.__dict__ = cclass["dict"]
return instance
return dct
def encode_(self, file):
with open(file, 'w') as outfile:
json.dump(
self.__dict__, outfile,
cls=JsonClassSerializable,
indent=4,
sort_keys=True
)
def decode_(self, file):
try:
with open(file, 'r') as infile:
self.__dict__ = json.load(
infile,
object_hook=self.json_to_class
)
except FileNotFoundError:
print("Persistence load failed "
"'{}' do not exists".format(file)
)
class C(JsonClassSerializable):
def __init__(self):
self.mill = "s"
JsonClassSerializable.register(C)
class B(JsonClassSerializable):
def __init__(self):
self.a = 1230
self.c = C()
JsonClassSerializable.register(B)
class A(JsonClassSerializable):
def __init__(self):
self.a = 1
self.b = {1, 2}
self.c = B()
JsonClassSerializable.register(A)
A().encode_("test")
b = A()
b.decode_("test")
print(b.a)
print(b.b)
print(b.c.a)
修改
通过更多的研究,我发现了一种使用元类
而不需要 SUPERCLASS 注册方法调用的方法import json
import collections
REGISTERED_CLASS = {}
class MetaSerializable(type):
def __call__(cls, *args, **kwargs):
if cls.__name__ not in REGISTERED_CLASS:
REGISTERED_CLASS[cls.__name__] = cls
return super(MetaSerializable, cls).__call__(*args, **kwargs)
class JsonClassSerializable(json.JSONEncoder, metaclass=MetaSerializable):
def default(self, obj):
if isinstance(obj, collections.Set):
return dict(_set_object=list(obj))
if isinstance(obj, JsonClassSerializable):
jclass = {}
jclass["name"] = type(obj).__name__
jclass["dict"] = obj.__dict__
return dict(_class_object=jclass)
else:
return json.JSONEncoder.default(self, obj)
def json_to_class(self, dct):
if '_set_object' in dct:
return set(dct['_set_object'])
elif '_class_object' in dct:
cclass = dct['_class_object']
cclass_name = cclass["name"]
if cclass_name not in REGISTERED_CLASS:
raise RuntimeError(
"Class {} not registered in JSON Parser"
.format(cclass["name"])
)
instance = REGISTERED_CLASS[cclass_name]()
instance.__dict__ = cclass["dict"]
return instance
return dct
def encode_(self, file):
with open(file, 'w') as outfile:
json.dump(
self.__dict__, outfile,
cls=JsonClassSerializable,
indent=4,
sort_keys=True
)
def decode_(self, file):
try:
with open(file, 'r') as infile:
self.__dict__ = json.load(
infile,
object_hook=self.json_to_class
)
except FileNotFoundError:
print("Persistence load failed "
"'{}' do not exists".format(file)
)
class C(JsonClassSerializable):
def __init__(self):
self.mill = "s"
class B(JsonClassSerializable):
def __init__(self):
self.a = 1230
self.c = C()
class A(JsonClassSerializable):
def __init__(self):
self.a = 1
self.b = {1, 2}
self.c = B()
A().encode_("test")
b = A()
b.decode_("test")
print(b.a)
# 1
print(b.b)
# {1, 2}
print(b.c.a)
# 1230
print(b.c.c.mill)
# s
答案 9 :(得分:1)
我在Flask应用中一直使用的一种方法,用于将Class实例序列化为JSON响应。
from json import JSONEncoder
import json
from typing import List
class ResponseEncoder(JSONEncoder):
def default(self, o):
return o.__dict__
class ListResponse:
def __init__(self, data: List):
self.data = data
self.count = len(data)
class A:
def __init__(self, message: str):
self.message = message
class B:
def __init__(self, record: A):
self.record = record
class C:
def __init__(self, data: B):
self.data = data
现在创建一个A,B,C实例,然后进行编码。
data_a = A('Test Data')
data_b = B(data_a)
data_c = C(data_b)
response = ResponseEncoder().encode(data_c)
json_response = json.loads(response)
输出
{
"data": {
"record": {
"message": "Test Data"
}
}
}
对于列表类型的响应
records = ['One', 'Two', 'Three']
list_response = ListResponse(records)
response = ResponseEncoder().encode(list_response)
json_response = json.loads(response)
输出
{
"data": [
"One",
"Two",
"Three"
],
"count": 3
}
答案 10 :(得分:1)
这里可以应用另一种非常简单而优雅的方法,即对 'dict' 进行子类化,因为它默认是可序列化的。
from json import dumps
class Response(dict):
def __init__(self, status_code, body):
super().__init__(
status_code = status_code,
body = body
)
r = Response()
dumps(r)
答案 11 :(得分:0)
如何开始这样做有一些很好的答案。但是有一些事情要记住:
__slots__
代替__dict__
json-tricks是一个图书馆(我做过,其他人做出了贡献)已经能够做了很长一段时间了。例如:
class MyTestCls:
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
cls_instance = MyTestCls(s='ub', dct={'7': 7})
json = dumps(cls_instance, indent=4)
instance = loads(json)
您将恢复您的实例。这里的json看起来像这样:
{
"__instance_type__": [
"json_tricks.test_class",
"MyTestCls"
],
"attributes": {
"s": "ub",
"dct": {
"7": 7
}
}
}
如果您想制作自己的解决方案,可以查看json-tricks
的来源,以免遗忘某些特殊情况(例如__slots__
)。
它还有其他类型,如numpy数组,日期时间,复数;它也允许评论。
答案 12 :(得分:0)
您可以使用 jsonic 将几乎所有内容序列化为JSON:
https://github.com/OrrBin/Jsonic
示例:
class TestClass:
def __init__(self):
self.x = 1
self.y = 2
instance = TestClass()
s = serialize(instance): # instance s set to: {"x":1, "y":2}
d = deserialize(s) # d is a new class instance of TestClass
pythonic 具有一些不错的功能,例如声明类属性为瞬态和类型安全的反序列化。
(答案迟了几年,但我认为这可能会对其他人有所帮助)
答案 13 :(得分:0)
使用任意的、可扩展的对象,然后将其序列化为JSON:
import json
class Object(object):
pass
response = Object()
response.debug = []
response.result = Object()
# Any manipulations with the object:
response.debug.append("Debug string here")
response.result.body = "404 Not Found"
response.result.code = 404
# Proper JSON output, with nice formatting:
print(json.dumps(response, indent=4, default=lambda x: x.__dict__))
答案 14 :(得分:0)
这可以通过 pydantic 轻松处理,因为它已经内置了此功能。
选项 1:正常方式
from pydantic import BaseModel
class testclass(BaseModel):
value1: str = "a"
value2: str = "b"
test = testclass()
>>> print(test.json(indent=4))
{
"value1": "a",
"value2": "b"
}
选项 2:使用 pydantic 的数据类
import json
from pydantic.dataclasses import dataclass
from pydantic.json import pydantic_encoder
@dataclass
class testclass:
value1: str = "a"
value2: str = "b"
test = testclass()
>>> print(json.dumps(test, indent=4, default=pydantic_encoder))
{
"value1": "a",
"value2": "b"
}
答案 15 :(得分:0)
我为此做了一个函数,效果很好:
def serialize(x,*args,**kwargs):
kwargs.setdefault('default',lambda x:getattr(x,'__dict__',dict((k,getattr(x,k) if not callable(getattr(x,k)) else repr(getattr(x,k))) for k in dir(x) if not (k.startswith('__') or isinstance(getattr(x,k),x.__class__)))))
return json.dumps(x,*args,**kwargs)