fer*_*han 160 python serialization json pickle
我正在尝试创建类实例的JSON字符串表示并且有困难.假设这个类是这样构建的:
class testclass:
value1 = "a"
value2 = "b"
Run Code Online (Sandbox Code Playgroud)
对json.dumps的调用是这样的:
t = testclass()
json.dumps(t)
Run Code Online (Sandbox Code Playgroud)
它失败并且告诉我测试类不是JSON可序列化的.
TypeError: <__main__.testclass object at 0x000000000227A400> is not JSON serializable
Run Code Online (Sandbox Code Playgroud)
我也尝试过使用pickle模块:
t = testclass()
print(pickle.dumps(t, pickle.HIGHEST_PROTOCOL))
Run Code Online (Sandbox Code Playgroud)
它提供类实例信息,但不提供类实例的序列化内容.
b'\x80\x03c__main__\ntestclass\nq\x00)\x81q\x01}q\x02b.'
Run Code Online (Sandbox Code Playgroud)
我究竟做错了什么?
ste*_*eha 214
基本问题是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}
Run Code Online (Sandbox Code Playgroud)
本博文中讨论了上述方法:
Bro*_*oli 51
有一种方法对我很有用,你可以尝试:
json.dumps()可以使用可选参数default,您可以为未知类型指定自定义序列化函数,在我的示例中看起来像
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__
Run Code Online (Sandbox Code Playgroud)
前两个ifs用于日期和时间序列化,然后obj.__dict__返回任何其他对象.
最后的电话看起来像:
json.dumps(myObj, default=serialize)
Run Code Online (Sandbox Code Playgroud)
当您序列化集合并且不希望__dict__为每个对象显式调用时,它尤其有用.这是为您自动完成的.
到目前为止,对我来说工作非常好,期待你的想法.
cod*_*n48 37
您可以default在json.dumps()函数中指定命名参数:
json.dumps(obj, default=lambda x: x.__dict__)
Run Code Online (Sandbox Code Playgroud)
说明:
``default(obj)`` is a function that should return a serializable version
of obj or raise TypeError. The default simply raises TypeError.
Run Code Online (Sandbox Code Playgroud)
(适用于Python 2.7和Python 3.x)
注意:在这种情况下,您需要instance变量而不是class变量,正如问题中的示例所尝试的那样.(我假设提问者意味着class instance成为一个班级的对象)
我首先从@ phihag的答案中了解到这一点.发现它是最简单,最干净的工作方式.
Spi*_*ail 22
我只是做:
data=json.dumps(myobject.__dict__)
Run Code Online (Sandbox Code Playgroud)
这不是完整的答案,如果你有某种复杂的对象类,你肯定不会得到所有东西.但是我将它用于我的一些简单对象.
它可以很好地工作的是从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)
Run Code Online (Sandbox Code Playgroud)
gie*_*s0r 14
import jsonpickle
object = YourClass()
json_object = jsonpickle.encode(object)
Run Code Online (Sandbox Code Playgroud)
JSON 并不是真正用于序列化任意 Python 对象。它非常适合序列化dict对象,但该pickle模块确实是您通常应该使用的。来自的输出pickle并不是真正的人类可读的,但它应该可以很好地解开。如果您坚持使用 JSON,您可以查看jsonpickle模块,这是一种有趣的混合方法。
https://github.com/jsonpickle/jsonpickle
Python3.x
以我的知识所能达到的最好的方法就是这个。
请注意,此代码也处理 set()。
这种方法是通用的,只需要类的扩展(在第二个例子中)。
请注意,我只是对文件进行操作,但根据您的喜好修改行为很容易。
然而,这是一个编解码器。
只需多做一点工作,您就可以以其他方式构建您的类。我假设有一个默认构造函数来实例化它,然后我更新类字典。
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)
Run Code Online (Sandbox Code Playgroud)
编辑
通过更多的研究,我找到了一种无需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
Run Code Online (Sandbox Code Playgroud)
这可以使用pydantic轻松处理,因为它已经内置了此功能。
方案一:正常方式
from pydantic import BaseModel
class testclass(BaseModel):
value1: str = "a"
value2: str = "b"
test = testclass()
>>> print(test.json(indent=4))
{
"value1": "a",
"value2": "b"
}
Run Code Online (Sandbox Code Playgroud)
选项 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"
}
Run Code Online (Sandbox Code Playgroud)
我相信,最好使用多态性,而不是接受的答案中建议的继承。否则,您必须有一个大的 if else 语句来自定义每个对象的编码。这意味着为 JSON 创建一个通用默认编码器,如下所示:
def jsonDefEncoder(obj):
if hasattr(obj, 'jsonEnc'):
return obj.jsonEnc()
else: #some default behavior
return obj.__dict__
Run Code Online (Sandbox Code Playgroud)
然后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
Run Code Online (Sandbox Code Playgroud)
然后你打电话json.dumps(classInstance,default=jsonDefEncoder)
这里有两个简单的函数,用于序列化任何不复杂的类,没有像前面解释的那样花哨。
我将它用于配置类型的东西,因为我可以在不调整代码的情况下向类添加新成员。
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}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
250372 次 |
| 最近记录: |