# Json.register()?

**URL:** <https://discuss.python.org/t/json-register/20289>\
**Category:** Ideas\
**Created:** [October 24, 2022, 6:52pm UTC](https://discuss.python.org/t/json-register/20289 "2022-10-24T18:52:01Z")\
**Posts on this page:** 16\
**Page:** 2

<div class="post-metadata">

**Author:** ![Marco\_Sulla](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/marco_sulla/32/1451_2.png) [@Marco\_Sulla](https://discuss.python.org/u/Marco_Sulla)\
**Post date:** [October 26, 2022, 7:40pm UTC](https://discuss.python.org/t/json-register/20289/21 "2022-10-26T19:40:32Z")

</div>

Well, I just realized that no one stops me to do this horrible thing:

```auto
>>> original_json_dumps = json.dumps
>>> _sentinel = object()
>>> 
>>> def my_json_dumps(*args, **kwargs):
... if args:
... obj = args[0]
... newargs = args[1:]
... else:
... obj = kwargs.pop(obj, _sentinel)
...         
... if obj is _sentinel:
... original_json_dumps()
...     
... if isinstance(obj, CustomTuple):
... return original_json_dumps(tuple(obj), *newargs, **kwargs)
... else if [...]
...     
... return original_json_dumps(obj, *newargs, **kwargs)
... 
>>> json.dumps = my_json_dumps

```

😛

---

<div class="post-metadata">

**Author:** ![pf\_moore](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pf_moore/32/35_2.png) [@pf\_moore](https://discuss.python.org/u/pf_moore)\
**Post date:** [October 26, 2022, 7:42pm UTC](https://discuss.python.org/t/json-register/20289/22 "2022-10-26T19:42:01Z")

</div>

Because it’s global state, and the alternative, as I’ve shown, is straightforward. I also (as a library author) don’t like the idea that a simple `json.dumps()` call might do something I wasn’t expecting because of something someone added to a registry (even though there’s plenty of _other_ ways people can cause unexpected effects in my code).

Why are _you_ so insistent that a registry is such a good idea? Is it that important to you that you can alter the meaning of `json.dumps()` calls that other people’s code makes?

---

<div class="post-metadata">

**Author:** ![Marco\_Sulla](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/marco_sulla/32/1451_2.png) [@Marco\_Sulla](https://discuss.python.org/u/Marco_Sulla)\
**Post date:** [October 26, 2022, 7:53pm UTC](https://discuss.python.org/t/json-register/20289/23 "2022-10-26T19:53:03Z")

</div>

> [@pf\_moore](#):
>
> Because it’s global state, and the alternative, as I’ve shown, is straightforward.

Not so straightforward, you have always to specify the `cls` parameter, and this is annoying.

> [@pf\_moore](#):
>
> Why are _you_ so insistent that a registry is such a good idea?

I’m not insistent, I just proposed it and I’m discussing it. I’m open to change my mind.

I think it’s a good idea because in my programmer’s life I wrote a lot of custom class that needed custom json serializer, and I found it very annoying to pass them every time. So usually I create a `to_json()` method, that sounds quite hackish to me, but it works. I believe this will be much more easy if you can simply register your own serializer.

---

<div class="post-metadata">

**Author:** ![bryevdv](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/bryevdv/32/7424_2.png) [@bryevdv](https://discuss.python.org/u/bryevdv)\
**Post date:** [October 26, 2022, 11:51pm UTC](https://discuss.python.org/t/json-register/20289/24 "2022-10-26T23:51:31Z")

</div>

I have to agree with @steven.daprano about the (very bad) issue for library authors. The _only_ way this could move up to “marginally acceptable” is if it came with a mechanism for library authors to forcibly disregard a registry entirely when they use the APIs internally (but that’s also pretty annoying to have to spend effort worrying about, to be honest).

---

<div class="post-metadata">

**Author:** ![jcgoble3](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jcgoble3/32/8094_2.png) [@jcgoble3](https://discuss.python.org/u/jcgoble3)\
**Post date:** [October 27, 2022, 12:37am UTC](https://discuss.python.org/t/json-register/20289/25 "2022-10-27T00:37:02Z")

</div>

Perhaps a compromise is to add a registry class that the user can register encoders to, but not create global state. Instead, one can pass the desired registry (stored locally) in as a parameter, which only needs to be done in one place thanks to `functools.partial`.

Example, in a `utils` module:

```auto
import functools
import json

registry = json.Registry()
registry.register(SpamType, SpamEncoder)
registry.register(EggsType, EggsEncoder)

jsondump = functools.partial(json.dumps, registry=registry)

```

And then in another module:

```auto
import utils # the module above

...

def myfunc(spam):
    ...
    json = utils.jsondump(spam)
    ...

```

---

<div class="post-metadata">

**Author:** ![methane](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/methane/32/43_2.png) [@methane](https://discuss.python.org/u/methane)\
**Post date:** [October 27, 2022, 4:45am UTC](https://discuss.python.org/t/json-register/20289/26 "2022-10-27T04:45:12Z")

</div>

No need to add `registry=` option. JSON has `default` option. `json.Registry()` object can work as default callable.

---

<div class="post-metadata">

**Author:** ![AndersMunch](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/andersmunch/32/12823_2.png) [@AndersMunch](https://discuss.python.org/u/AndersMunch)\
**Post date:** [October 27, 2022, 11:21am UTC](https://discuss.python.org/t/json-register/20289/27 "2022-10-27T11:21:38Z")

</div>

> [@Marco\_Sulla](#):
>
> I believe this will be much more easy if you can simply register your own serializer.

But how will you deserialize? I’m skeptical of making extensive customisation to `json.dumps` without having a concept for how `json.loads` will get an object of the original type back.

---

<div class="post-metadata">

**Author:** ![steven.daprano](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steven.daprano/32/1083_2.png) [@steven.daprano](https://discuss.python.org/u/steven.daprano)\
**Post date:** [October 27, 2022, 12:39pm UTC](https://discuss.python.org/t/json-register/20289/28 "2022-10-27T12:39:29Z")

</div>

> [@](#):
>
> But how will you deserialize?

The same way we deserialize right now: by supplying a custom decoder to `json.loads`.

How do we know which decoder to use? (A registry won’t help.) Same way we do now: by tracking the information out of band.

I think this deserialization problem is a red herring. It is no worse than the deserialization problem for `json.dumps(cls=Encoder)`, which has the solution `json.loads(cls=Decoder)`.

---

<div class="post-metadata">

**Author:** ![apalala](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/apalala/32/683_2.png) [@apalala](https://discuss.python.org/u/apalala)\
**Post date:** [November 2, 2022, 2:42pm UTC](https://discuss.python.org/t/json-register/20289/29 "2022-11-02T14:42:10Z")

</div>

TatSu uses `asjson()` (which should probably be `as_json_compatible()`), and honors `def _json__()` when converting the argument to something compatible with `json.dumps()`.

There’s no support for deserialization there.

I’ve used the same strategy elsewhere _with_ support for deserialization.

I don’t think that the `json` module should be burdened with what seems as program-specific requirements. Developers can write their customized `to_json()` and `from_json()` functions.

---

<div class="post-metadata">

**Author:** ![Marco\_Sulla](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/marco_sulla/32/1451_2.png) [@Marco\_Sulla](https://discuss.python.org/u/Marco_Sulla)\
**Post date:** [November 2, 2022, 7:00pm UTC](https://discuss.python.org/t/json-register/20289/30 "2022-11-02T19:00:21Z")

</div>

> [@methane](#):
>
> No need to add `registry=` option. JSON has `default` option. `json.Registry()` object can work as default callable.

The problem is always that you have to specify every time the `default` argument. It’s the same problem that you have with `cls`.

---

<div class="post-metadata">

**Author:** ![Marco\_Sulla](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/marco_sulla/32/1451_2.png) [@Marco\_Sulla](https://discuss.python.org/u/Marco_Sulla)\
**Post date:** [November 2, 2022, 7:06pm UTC](https://discuss.python.org/t/json-register/20289/31 "2022-11-02T19:06:25Z")

</div>

> [@AndersMunch](#):
>
> But how will you deserialize? I’m skeptical of making extensive customisation to `json.dumps` without having a concept for how `json.loads` will get an object of the original type back.

Well, calling a simple register an “extensive” customization seems to me an exaggeration.

About `json.loads`, as we already said, can’t have a registry, for the simple fact that JSON does not have a default field in which you can store the object type, as `pickle` does. As I already said, `json.dumps` and `json.loads` are not symmetric, and my propose is only for `json.dump(s)`

---

<div class="post-metadata">

**Author:** ![Marco\_Sulla](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/marco_sulla/32/1451_2.png) [@Marco\_Sulla](https://discuss.python.org/u/Marco_Sulla)\
**Post date:** [November 2, 2022, 7:11pm UTC](https://discuss.python.org/t/json-register/20289/32 "2022-11-02T19:11:27Z")

</div>

> [@apalala](#):
>
> I don’t think that the `json` module should be burdened with what seems as program-specific requirements.

And `pickle`? ` __reduce__ ` make it program-specific?

---

<div class="post-metadata">

**Author:** ![cameron](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/cameron/32/2658_2.png) [@cameron](https://discuss.python.org/u/cameron)\
**Post date:** [November 2, 2022, 10:30pm UTC](https://discuss.python.org/t/json-register/20289/33 "2022-11-02T22:30:20Z")

</div>

No, but the plain `json.register(type,encoder)` suggested uses a single  
_global_ registry for _all_ JSON encoding. A disaster.

The existing `JSONEncoder` class has a variety of hooks for special  
encoding of things. Maybe it should be easier to use somehow, but at  
least its use is overt and not shared in an uncoordinated way between  
multiple unrelated JSON-using libraries.

Cheers,  
Cameron Simpson [cs@cskk.id.au](mailto:cs@cskk.id.au)

---

<div class="post-metadata">

**Author:** ![apalala](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/apalala/32/683_2.png) [@apalala](https://discuss.python.org/u/apalala)\
**Post date:** [November 3, 2022, 11:47am UTC](https://discuss.python.org/t/json-register/20289/34 "2022-11-03T11:47:19Z")

</div>

> [@Marco\_Sulla](#):
>
> And `pickle`? ` __reduce__ ` make it program-specific?

With ` __reduce__ ()` the class is following a protocol with effect on a limited context (instances of the class). The same could be had for JSON with a ` __json__ ()` protocol.

A global registry risks unintended effects on _any_ library used by a program (you could register `object`).

For reference, this is what is currently done in TatSu:

```python
def asjson(obj, seen=None): # pylint: disable=too-many-return-statements,too-many-branches
    if isinstance(obj, Mapping) or isiter(obj):
        # prevent traversal of recursive structures
        if seen is None:
            seen = set()
        elif id(obj) in seen:
            return ' __RECURSIVE__'
        seen.add(id(obj))

    if isinstance(obj, (weakref.ReferenceType, weakref.ProxyType)):
        return f'@0x{hex(id(obj)).upper()[2:]}'
    elif hasattr(obj, ' __json__'):
        return obj. __json__ ()
    elif is_namedtuple(obj):
        return asjson(obj._asdict(), seen=seen)
    elif isinstance(obj, Mapping):
        result = {}
        for k, v in obj.items():
            try:
                result[k] = asjson(v, seen)
            except TypeError:
                debug('Unhashable key?', type(k), str(k))
                raise
        return result
    elif isinstance(obj, uuid.UUID):
        return str(obj)
    elif isinstance(obj, enum.Enum):
        return obj.value
    elif isiter(obj):
        return [asjson(e, seen) for e in obj]
    else:
        return obj

```

---

<div class="post-metadata">

**Author:** ![Marco\_Sulla](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/marco_sulla/32/1451_2.png) [@Marco\_Sulla](https://discuss.python.org/u/Marco_Sulla)\
**Post date:** [February 5, 2023, 7:51am UTC](https://discuss.python.org/t/json-register/20289/35 "2023-02-05T07:51:47Z")

</div>

`pickle` has a global register for encoders. You can register your own encoder with `copyreg`:

> **[copyreg — Register pickle support functions](https://docs.python.org/3/library/copyreg.html)**
>
> Source code: Lib/copyreg.py The copyreg module offers a way to define functions used while pickling specific objects. The pickle and copy modules use those functions when pickling/copying those obj...

So it seems to me that a global register is not a disaster at all.

Third party libraries are, well, not `json`? They do not use `json` and so why they will be affected by a global register for `json`?

---

<div class="post-metadata">

**Author:** ![Marco\_Sulla](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/marco_sulla/32/1451_2.png) [@Marco\_Sulla](https://discuss.python.org/u/Marco_Sulla)\
**Post date:** [February 5, 2023, 8:04am UTC](https://discuss.python.org/t/json-register/20289/36 "2023-02-05T08:04:03Z")

</div>

**Unintended effects** : If you fear unintended effects by third party code, don’t use Python:

> [@Json.register()?](https://discuss.python.org/t/json-register/20289/21):
>
> Well, I just realized that no one stops me to do this horrible thing: \>\>\> original\_json\_dumps = json.dumps \>\>\> \_sentinel = object() \>\>\> \>\>\> def my\_json\_dumps(\*args, \*\*kwargs): ... if args: ... obj = args[0] ... newargs = args[1:] ... else: ... obj = kwargs.pop(obj, \_sentinel) ... ... if obj is \_sentinel: ... original\_json\_dumps() ... ... if isinstance(obj, CustomTuple): ... return original\_json\_dumps(tuple(obj), \*new…

**json magic method** : If we introduce ` __json__ ()`, why not ` __xml__ ()`? And ` __yaml__ ()`? And so on.

**global registry** : see my previous post.

[Previous page](https://discuss.python.org/t/json-register/20289.md?page=1)
