我想创建一个表现为命名元组的类型,除了它具有自定义表示形式外,当序列化为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)
现在print(MyPair('a', 'b'))
将按预期输出a:b
,但是print(json.dumps([MyPair('a', 'b')], cls=MyJSONEncoder))
会产生[["a", "b"]]
,因为default()
仅在对象最初不是可序列化为JSON的情况下才被调用。由于我自己的类型是元组,因此在我有机会进行干预之前,它将被序列化。
在没有将MyPair
设为Tuple
或在替换所有MyPair
对象的预处理步骤中遍历整个文档的情况下,是否有任何一种不错的方法或不错的方法来实现此目的通过字符串?
编辑:为了解决Joran的答案,我仍然想保留对偶尔包含MyPair
的复杂树进行序列化的功能。我的最小示例可能并没有说明清楚,抱歉。
答案 0 :(得分:2)
仅包含默认参数
def my_class_encoder(o):
if isinstance(o,MyClass):
return repr(o)
json.dumps(myClassInstance,default=my_class_encoder)
比真正的编码器更容易处理
....
但实际上只是在您的课程中添加一个定义
class MyPair(NamedTuple):
left: str
right: str
def serialize(self):
return list(self)
def __repr__(self):
return self.left + ':' + self.right
然后是
json.dumps(myClassInstance.serialize())
这样做的好处是可以更加清楚自己的操作(至少是恕我直言)
答案 1 :(得分:0)
所以我最终从头开始或多或少地重新实现了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)
对于自定义类型,将类型名称和将其字符串化的函数添加到self._serializers
中,您应该会很好。 iterencode()
的行为与正常行为不同(主要是因为它分开产生括号,而不是与第一个或最后一个元素并排出现),但是我看不到这会破坏任何地方。