Ah, that makes sense, thank you for the detailed example and explanation. I had only read through the start of the previous topic, as it started to become a bit difficult to follow (largely due to the author being non-specific with what precisely they were looking for and the discussion forking off in several directions).

> 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

> 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

I could imagine the default function becoming quite convoluted over time, depending on the complexity and number of individual classes needed. There definitely seems to be an advantage to specifying the behavior on a per-class basis to keep things more organized when necessary.

> If you read the simplejson docs, and search for code that imports it, you can probably find lots of real-life examples.

I'll be sure to look over it. For the most part, stdlib's json module has suited most of my purposes, but it would definitely be useful to know some of the other implementations that allow for more extensive typing and customization.

> So, I’m not sure this change is necessary. But I don’t see anything wrong with it if people really want it.

It doesn't seem to be needed, but it could definitely help with providing better organization and readability in the right situations. The simplicity is definitely appealing.

Thanks,
Kyle Stanley

On Sat, Aug 17, 2019 at 3:27 AM Andrew Barnert <abarnert@yahoo.com> wrote:
On Aug 16, 2019, at 20:35, Kyle Stanley <aeros167@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.