I'd like to create a type that behaves as a named tuple except that it has a custom representation, which is also respected when serialized as JSON.
The naive by-the-books approach would be something like this:
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)
Now print(MyPair('a', 'b')) will output a:b as intended, but print(json.dumps([MyPair('a', 'b')], cls=MyJSONEncoder)) will produce [["a", "b"]] because default() is only called if an object is not primitively serializable as JSON. Since my own type is a tuple, it will be serialized before I get a chance to intervene.
Is there any nice or not-so-nice way of achieving this without making MyPair not a Tuple or iterating over the entire document in a preprocessing step that replaces all MyPair objects by strings?
Edit: To address Joran's answer, I still want to retain the ability to serialize complex trees that just contain the occasional MyPairs. My minimal example might not have made that clear, sorry.