On Aug 16, 2019, at 20:35, Kyle Stanley <[email protected]> wrote:
>
> Speaking of examples, I think it would be helpful to provide a brief example
> of ``object.__json__()`` being used for some added clarity. Would it be a
> direct alias of ``json.dumps()`` with the same exact parameters and usage, or
> would there be some substantial differences?
No, the idea is that it’s _used_ by dumps, as a substitute for passing a
default function as an argument.
Let’s say you have a tree class, and you want to serialize it to JSON as an
array of arrays of etc. Here’s how you do it today:
class MyTreeNode:
# …
def data(self) -> int:
# …
def children(self) -> List[MyTreeNode]:
# …
def jsonize_my_tree(obj):
if isinstance(obj, MyTreeNode):
return [obj.data(), *map(jsonize_my_tree, obj.children())]
raise TypeError()
myjson = json.dumps(my_thing_that_might_include_trees,
default=jsonize_my_tree)
This is only mildly inconvenient. But it does become worse if you have lots of
classes you want to serialize. You need to write a default function somewhere
that knows about all of your classes:
def jsonize_my_things(obj):
if isinstance(obj, MyTreeNode):
return [obj.data(), jsonize_my_tree(obj.children())]
if isinstance(obj, MyRational):
return {'Fraction', obj.numerator, obj.denominator}
if isinstance(obj, MyWinUTF16String):
return str(obj)
# …
raise TypeError()
Or you need to come up with a registry, or a protocol, or a singledispatch
overload set, or some other way to let you write a separate function for each
class and automatically combine them into one function you can pass to the
default argument.
The __json__ method would be a pre-made protocol that lets you just write the
special handler for each class as part of that class, and not worry about how
to combine them because the json module already takes care of that. So:
class MyTreeNode:
# …
def data(self) -> int:
# …
def children(self) -> List[MyTreeNode]:
# …
def __json__(self):
return [obj.data(), *map(jsonize_my_tree, obj.children())]
class MyRational:
# …
def __json__(self):
return {'Fraction', obj.numerator, obj.denominator}
myjson =
json.dumps(my_thing_that_might_include_trees_and_rationals_and_who_knows_what_else)
Not a huge win with a single class, but with lots of classes you want custom
serialization for, it can be very handy.
Some of the third-party JSON modules, like simplejson, provide exactly this
functionality (usually under the name for_json, because __json__ has double
underscores and is therefore reserved for Python and its stdlib), and there are
definitely people who like it. If you read the simplejson docs, and search for
code that imports it, you can probably find lots of real-life examples.
As I mentioned earlier in the thread, it’s very easy to write your own default
function that calls for_json and partial that into dump/dumps/JSONEncoder (or
to write a subclass of JSONEncoder that does the same thing without needing a
default argument). And that has some nice benefits if you want to customize
things (e.g., pass some of the encoder arguments into the for_json call), or if
you prefer a registry or overload set to a method protocol, or whatever. So,
I’m not sure this change is necessary. But I don’t see anything wrong with it
if people really want it.
_______________________________________________
Python-ideas mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/PTI5XMPFF473PMESGK7AGX6NEPKVCNTY/
Code of Conduct: http://python.org/psf/codeofconduct/