On Mon, Apr 6, 2020, 3:37 PM Dan Cojocaru <dan.cojocar...@e-uvt.ro> wrote:

> I don't see why the need for standardisation exists.
>
> Here's another example because I can explain myself best by giving
> examples:
>
> I have an API. I can write some classes that correspond to JSON inputs my
> API expects and define __json__ in them to convey how they are expected to
> be serialised. For example, a Person class with first_name and last_name
> fields being serialised as a dict with the fields in camel case instead.
> (Sure, normally I'd write a serialisation function as well as part of my
> package in this case but I'm just coming up with examples on the spot).
>

DRF (Django REST API framework) serializers have a .to_representation()
method:
> Takes the object instance that requires serialization, and should return
a primitive representation. Typically this means returning a structure of
built-in Python datatypes. The exact types that can be handled will depend
on the render classes you have configured for your API.
>
> May be overridden in order to modify the representation style
https://www.django-rest-framework.org/api-guide/serializers/#overriding-serialization-and-deserialization-behavior


> For non standard things like date and time, many implementations of JSON
> already added support for it using a format I can't recall at the moment.
> And even if a class defines __json__ in a particular format but you would
> need another one, the method of making a custom JSONEncoder still remains,
> as __json__ functions would be attempted only after the custom default
> implementation. Therefore, __json__ would enable sensible defaults to be
> chosen while not harming the possibility of using a custom format if it is
> so desired.
>

In trying to do this (again), I've realized that you *can't* just check for
hasattr(obj, '__json__') in a JSONEncoder.default method because default
only get's called for types it doesn't know how to serialize; so if you
e.g. subclass a dict, default() never gets called for the dict subclass.

https://docs.python.org/3/library/json.html :
> If specified, default should be a function that gets called for objects
that can’t otherwise be serialized.

https://github.com/python/cpython/blob/master/Lib/json/encoder.py

https://stackoverflow.com/questions/16405969/how-to-change-json-encoding-behaviour-for-serializable-python-object/17684652#17684652
specifies how to overload _make_iterencode *in pure Python*, but AFAICS
that NOPs use of the C-optimized json encoder.

The python json module originally came from simplejson.

Here's simplejson's for_json implementation in pure Python:
https://github.com/simplejson/simplejson/blob/288e4e005c39a2eb855b5225c5dc8ebcb82eee72/simplejson/encoder.py#L515
:

for_json = _for_json and getattr(value, 'for_json', None)
> if for_json and callable(for_json):
>     chunks = _iterencode(for_json(), _current_indent_level)
>

And simplejson's for_json implementation in C:
https://github.com/simplejson/simplejson/blob/288e4e005c39a2eb855b5225c5dc8ebcb82eee72/simplejson/_speedups.c#L2839

Is there a strong reason that the method would need to be called __json__
instead of 'for_json'?

Passing a spec=None kwarg through to the __json__()/for_json() method would
not be compatible with simplejson's existing implementation.
Passing a spec=None kwarg would be necessary to support different JSON
standards within the same method; which I think is desirable because
JSON/JSON5/JSONLD/JSON_future.


>
> ---
> Dan Cojocaru
> On 6 Apr 2020, 22:08 +0300, Chris Angelico <ros...@gmail.com>, wrote:
>
> On Tue, Apr 7, 2020 at 4:48 AM Andrew Barnert via Python-ideas
> <python-ideas@python.org> wrote:
>
> That’s not true for JSON; the entire point of it is data interchange. You
> expect to be able to dump an object, send it over the wire or store it to a
> file, load it (or even parse it in JS or ObjC or Go or whatever) and get
> back an equivalent object. It’s easy to come up with ways to build on top
> of JSON to interchange things like time points or raw binary strings or
> higher-level structured objects, but they require doing something on both
> the encode side and the decode side. Just being able to encode them to
> something human-readable is useless—if I encode a datetime object, I need
> to get back a datetime (or Date or NSDate or whatever) on the other end,
> not a str (or string or NSString or whatever) that a human could tell is
> probably meant to be a datetime but will raise an exception when I try to
> subtract it from now().
>
> (Of course JSON isn’t perfect, as anyone who’s tried to interchange, say,
> int64 values discovers… but it’s good enough for many applications.)
>
>
> The trouble with using JSON to interchange things that aren't in the
> JSON spec is that you then have to build another layer on top of it -
> a non-standard layer. That means that whatever you do, it's inherently
> app-specific. For instance, I might choose to encode a datetime as a
> string in ISO format, but how is the other end going to know that it
> was meant to be a date? (Usually, if I'm sending something to some
> front-end JavaScript, I'll just hard-code the JS to know that
> thing[0].created is a timestamp, and should be parsed accordingly.)
>
> So if it's app-specific, then the best way to handle it is in your
> app, not in the data type you're encoding. Subclassing JSONEncoder
> works for this; adding a __json__ method doesn't really, unless there
> is some single canonical encoding for a particular object.
>
> The two become close to equivalent when you're only asking about your
> own custom classes. You can, in fact, create your own private __json__
> protocol (although, since it's private to you, it'd probably be better
> to call the method "to_json" rather than "__json__"), and have a
> subclass of JSONEncoder that calls it. It'd work fine because you
> don't NEED to interoperate with other libraries. It's only when you
> try to standardize something that's inherently nonstandard that things
> get problematic :)
>
> ChrisA
> _______________________________________________
> 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/4ZEQKTQ5CMZUXC6E6JVFHQS3VGYEDJ7C/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
> _______________________________________________
> 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/7LTMRW3IL4A4U3VYDZBZEXBLGOMTSFH4/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
_______________________________________________
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/6GEZ2ZRE6JOUA4UOMIQE4QKBAW7O23N7/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to