序列化嵌套 类
Serializing nested classes
我正在尝试为 dataclass
创建一个 custom JSON encoder,但 class 实际上嵌入在另一个 class 中,顶部 class 正在连载中。我的 class 定义是这样的:
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class Foo():
foo_member: int = 1
@property
def a_property(self):
return self.foo_member+1
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class Bar():
foo_list: List[Foo] = field(default_factory=list)
我的整个测试代码是:
from dataclasses import dataclass, field, asdict, is_dataclass
from typing import List
from json import JSONEncoder
from pprint import pprint
class FooJsonEncoder(JSONEncoder):
'''
This should be used exclusively for encoding the ELF metadata as KDataFormat
is treated differently here.
'''
def custom(self, x):
print(f'custom type {type(x)}')
if isinstance(x, list):
print(f'here {dict(x)}')
pprint(x)
if isinstance(x, Foo):
d = asdict(x)
d['a_property'] = getattr(x, 'a_property')
return d
elif is_dataclass(x):
return asdict(x)
return dict(x)
def default(self, o):
print(f'default type {type(o)}')
if isinstance(o, Foo):
d = asdict(o)
d['a_property'] = getattr(o, 'a_property')
return d
elif is_dataclass(o):
return asdict(o, dict_factory=self.custom)
return super(FooJsonEncoder, self).default(o)
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class Foo():
foo_member: int = 1
@property
def a_property(self):
return self.foo_member+1
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class Bar():
foo_list: List[Foo] = field(default_factory=list)
def main():
foo1 = Foo(1)
foo2 = Foo(2)
assert 2 == foo1.a_property
assert 3 == foo2.a_property
bar = Bar(foo_list=[foo1, foo2])
print(FooJsonEncoder().encode(bar))
if __name__ == "__main__":
main()
当我运行它时,我得到
default type <class '__main__.Bar'>
custom type <class 'list'>
here {'foo_member': 1}
[('foo_member', 1)]
custom type <class 'list'>
here {'foo_member': 2}
[('foo_member', 2)]
custom type <class 'list'>
here {'foo_list': [{'foo_member': 1}, {'foo_member': 2}]}
[('foo_list', [{'foo_member': 1}, {'foo_member': 2}])]
{"foo_list": [{"foo_member": 1}, {"foo_member": 2}]}
我的 FooJsonEncoder.default
被 main
调用了一次。有趣的是 FooJsonEncoder.custom
是用拆分列表而不是两个 Foo
对象的列表调用的:
custom type <class 'list'>
here {'foo_member': 1}
[('foo_member', 1)]
custom type <class 'list'>
here {'foo_member': 2}
[('foo_member', 2)]
然后用两个成员的列表调用,但已经转换为 dict
:
custom type <class 'list'>
here {'foo_list': [{'foo_member': 1}, {'foo_member': 2}]}
[('foo_list', [{'foo_member': 1}, {'foo_member': 2}])]
{"foo_list": [{"foo_member": 1}, {"foo_member": 2}]}
一旦 return dict(x)
在 custom
中被调用,我就无法对嵌套的 class.
使用自定义转换
嵌套 class 时如何传递自定义 JSON 序列化程序?
谢谢。
我认为问题在于 asdict 是递归的,但不允许您访问中间的步骤。所以一旦你点击 bar asdict 接管并序列化所有数据类。可能有一种方法可以使 a_property
成为一个字段并回避这个问题。
但我只是手动将数据类转换为字典,这样我就可以添加额外的字段。您只需要知道 asdict 实际上会复制列表,而这不会那样做,但是不需要它,因为它都被序列化为字符串。
from dataclasses import (
dataclass,
field,
is_dataclass,
fields)
from typing import List
from json import JSONEncoder
from pprint import pformat
class FooJsonEncoder(JSONEncoder):
def default(self, obj):
print ('serializing {}'.format(pformat(obj)))
if is_dataclass(obj):
print (' is dataclass')
fieldnames = [f.name for f in fields(obj)]
if isinstance(obj, Foo):
print (' is Foo')
fieldnames.append('a_property')
d = dict([(name, getattr(obj, name)) for name in fieldnames])
print(' serialized to {}'.format(pformat(d)))
return d
else:
pprint (' is not dataclass')
res = super(FooJsonEncoder, self).default(obj)
print(' serialized to {}'.format(pformat(res)))
return res
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class Foo():
foo_member: int = 1
@property
def a_property(self):
return self.foo_member+1
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class Bar():
foo_list: List[Foo] = field(default_factory=list)
def main():
foo1 = Foo(1)
foo2 = Foo(2)
assert 2 == foo1.a_property
assert 3 == foo2.a_property
bar = Bar(foo_list=[foo1, foo2])
print(FooJsonEncoder().encode(bar))
if __name__ == "__main__":
main()
我正在尝试为 dataclass
创建一个 custom JSON encoder,但 class 实际上嵌入在另一个 class 中,顶部 class 正在连载中。我的 class 定义是这样的:
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class Foo():
foo_member: int = 1
@property
def a_property(self):
return self.foo_member+1
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class Bar():
foo_list: List[Foo] = field(default_factory=list)
我的整个测试代码是:
from dataclasses import dataclass, field, asdict, is_dataclass
from typing import List
from json import JSONEncoder
from pprint import pprint
class FooJsonEncoder(JSONEncoder):
'''
This should be used exclusively for encoding the ELF metadata as KDataFormat
is treated differently here.
'''
def custom(self, x):
print(f'custom type {type(x)}')
if isinstance(x, list):
print(f'here {dict(x)}')
pprint(x)
if isinstance(x, Foo):
d = asdict(x)
d['a_property'] = getattr(x, 'a_property')
return d
elif is_dataclass(x):
return asdict(x)
return dict(x)
def default(self, o):
print(f'default type {type(o)}')
if isinstance(o, Foo):
d = asdict(o)
d['a_property'] = getattr(o, 'a_property')
return d
elif is_dataclass(o):
return asdict(o, dict_factory=self.custom)
return super(FooJsonEncoder, self).default(o)
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class Foo():
foo_member: int = 1
@property
def a_property(self):
return self.foo_member+1
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class Bar():
foo_list: List[Foo] = field(default_factory=list)
def main():
foo1 = Foo(1)
foo2 = Foo(2)
assert 2 == foo1.a_property
assert 3 == foo2.a_property
bar = Bar(foo_list=[foo1, foo2])
print(FooJsonEncoder().encode(bar))
if __name__ == "__main__":
main()
当我运行它时,我得到
default type <class '__main__.Bar'>
custom type <class 'list'>
here {'foo_member': 1}
[('foo_member', 1)]
custom type <class 'list'>
here {'foo_member': 2}
[('foo_member', 2)]
custom type <class 'list'>
here {'foo_list': [{'foo_member': 1}, {'foo_member': 2}]}
[('foo_list', [{'foo_member': 1}, {'foo_member': 2}])]
{"foo_list": [{"foo_member": 1}, {"foo_member": 2}]}
我的 FooJsonEncoder.default
被 main
调用了一次。有趣的是 FooJsonEncoder.custom
是用拆分列表而不是两个 Foo
对象的列表调用的:
custom type <class 'list'>
here {'foo_member': 1}
[('foo_member', 1)]
custom type <class 'list'>
here {'foo_member': 2}
[('foo_member', 2)]
然后用两个成员的列表调用,但已经转换为 dict
:
custom type <class 'list'>
here {'foo_list': [{'foo_member': 1}, {'foo_member': 2}]}
[('foo_list', [{'foo_member': 1}, {'foo_member': 2}])]
{"foo_list": [{"foo_member": 1}, {"foo_member": 2}]}
一旦 return dict(x)
在 custom
中被调用,我就无法对嵌套的 class.
嵌套 class 时如何传递自定义 JSON 序列化程序?
谢谢。
我认为问题在于 asdict 是递归的,但不允许您访问中间的步骤。所以一旦你点击 bar asdict 接管并序列化所有数据类。可能有一种方法可以使 a_property
成为一个字段并回避这个问题。
但我只是手动将数据类转换为字典,这样我就可以添加额外的字段。您只需要知道 asdict 实际上会复制列表,而这不会那样做,但是不需要它,因为它都被序列化为字符串。
from dataclasses import (
dataclass,
field,
is_dataclass,
fields)
from typing import List
from json import JSONEncoder
from pprint import pformat
class FooJsonEncoder(JSONEncoder):
def default(self, obj):
print ('serializing {}'.format(pformat(obj)))
if is_dataclass(obj):
print (' is dataclass')
fieldnames = [f.name for f in fields(obj)]
if isinstance(obj, Foo):
print (' is Foo')
fieldnames.append('a_property')
d = dict([(name, getattr(obj, name)) for name in fieldnames])
print(' serialized to {}'.format(pformat(d)))
return d
else:
pprint (' is not dataclass')
res = super(FooJsonEncoder, self).default(obj)
print(' serialized to {}'.format(pformat(res)))
return res
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class Foo():
foo_member: int = 1
@property
def a_property(self):
return self.foo_member+1
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class Bar():
foo_list: List[Foo] = field(default_factory=list)
def main():
foo1 = Foo(1)
foo2 = Foo(2)
assert 2 == foo1.a_property
assert 3 == foo2.a_property
bar = Bar(foo_list=[foo1, foo2])
print(FooJsonEncoder().encode(bar))
if __name__ == "__main__":
main()