On Aug 16, 2019, at 20:35, Kyle Stanley <aeros...@gmail.com> 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 -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/PTI5XMPFF473PMESGK7AGX6NEPKVCNTY/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to