Chr*_*ian 5 python json types python-3.x
我想创建一个行为类似于命名元组的类型,只不过它具有自定义表示形式,在序列化为 JSON 时也受到尊重。
天真的按书本方法是这样的:
from typing import NamedTuple
import json
class MyPair(NamedTuple):
left: str
right: str
def __repr__(self):
return self.left + ':' + self.right
class MyJSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, MyPair):
return str(obj)
return json.JSONEncoder.default(self, obj)
Run Code Online (Sandbox Code Playgroud)
现在将按预期print(MyPair('a', 'b'))输出,但会产生,因为仅当对象不能原始序列化为 JSON 时才被调用。由于我自己的类型是一个元组,因此在我有机会干预之前它将被序列化。a:bprint(json.dumps([MyPair('a', 'b')], cls=MyJSONEncoder))[["a", "b"]]default()
是否有任何好的或不太好的方法来实现此目的,而无需在用字符串替换所有对象的预处理步骤中MyPair不进行或迭代整个文档?TupleMyPair
编辑:为了解决 Joran 的答案,我仍然想保留序列化仅包含偶尔MyPairs 的复杂树的能力。我的最小例子可能没有说清楚,抱歉。
所以我最终或多或少地从头开始重新实现了 JSONEncoder。因为我不需要任何花哨的漂亮打印,所以这相当简单:
class MyJSONEncoder(json.JSONEncoder):
def __init__(self, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False,
indent=None, separators=None, default=None):
super().__init__(skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular,
allow_nan=allow_nan, sort_keys=sort_keys, indent=indent, separators=separators,
default=default)
self._serializers: Set[Tuple[Type, Callable]] = {
(MyPair, lambda pair: '"' + str(pair) + '"',)
}
def default(self, o):
return super().default(o)
def encode(self, o):
return ''.join(self.iterencode(o))
def iterencode(self, o, _one_shot=False):
for t, serializer in self._serializers:
if isinstance(o, t):
yield serializer(o)
break
else:
if isinstance(o, bool):
yield "true" if o else "false"
elif isinstance(o, str):
yield '"' + o + '"'
elif isinstance(o, bytes):
yield '"' + o.decode("utf-8") + '"'
elif isinstance(o, int) or isinstance(o, float) or isinstance(o, Decimal):
yield str(o)
elif isinstance(o, Dict):
yield '{'
for num, (key, value) in enumerate(o.items()):
yield bool(num) * ', ' + '"' + str(key) + '": '
yield from self.iterencode(value)
yield '}'
elif isinstance(o, Sequence):
yield '['
for num, value in enumerate(o):
yield bool(num) * ', '
yield from self.iterencode(value)
yield ']'
else:
yield self.default(o)
Run Code Online (Sandbox Code Playgroud)
对于自定义类型,添加类型名称和将其字符串化的函数self._serializers就可以了。其iterencode()行为与普通的不同(主要是它单独产生括号,而不是与第一个或最后一个元素并排),但我看不出这会在哪里破坏任何东西。