我试图创建一个类实例的JSON字符串表示,有困难。假设这个类是这样构建的:
class testclass:
value1 = "a"
value2 = "b"
对json的调用。转储是这样的:
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.'
我做错了什么?
你可以在json.dumps()函数中指定默认的命名参数:
json.dumps(obj, default=lambda x: x.__dict__)
解释:
从文档(2.7,3.6):
``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)
注意:在这种情况下,您需要实例变量而不是类变量,正如问题中的示例所尝试做的那样。(我假设请求者意味着类实例是类的对象)
我首先从@phihag的回答中了解到这一点。发现这是最简单、最干净的工作方式。
我为此做了一个函数,效果很好:
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)
我认为,与其像公认的答案中建议的那样继承,不如使用多态。否则你必须有一个大的if else语句来自定义每个对象的编码。这意味着为JSON创建一个通用的默认编码器:
def jsonDefEncoder(obj):
if hasattr(obj, 'jsonEnc'):
return obj.jsonEnc()
else: #some default behavior
return obj.__dict__
然后在你想序列化的每个类中都有一个jsonEnc()函数。如。
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)
这可以用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"
}
这里有一些关于如何开始做这件事的很好的答案。但有一些事情要记住:
如果实例嵌套在大型数据结构中怎么办?
如果还想要类名呢?
如果要反序列化实例该怎么办?
如果你使用__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数组,datetimes,复数;它还允许注释。