#22821: DjangoJSONEncoder no longer supports simplejson
-------------------------------------+-------------------------------------
Reporter: Keryn Knight | Owner: nobody
<django@…> | Status: new
Type: New feature | Version: master
Component: Core | Resolution:
(Serialization) | Triage Stage:
Severity: Normal | Unreviewed
Keywords: | Needs documentation: 0
Has patch: 0 | Patch needs improvement: 0
Needs tests: 0 | UI/UX: 0
Easy pickings: 0 |
-------------------------------------+-------------------------------------
Comment (by Keryn Knight <django@…>):
Perhaps I'm not understanding the nuances of the problem (it wouldn't be
the first time), but I'm not convinced that solving this particular
problem is difficult (periphery around it, perhaps)
The following code-bomb works for me using `simplejson` versions (the last
''PATCH'' releases of every ''MINOR'' version):
* 2.1.6
* 2.6.2
* 3.0.9
* 3.3.3
* 3.4.1
* 3.5.2
and the `json` module in Python 2.7.6 (I'm guessing/hoping the least Py3K
compatible part of this is using `xrange`, and `print` as a keyword)
{{{
import simplejson
import json
import decimal
import datetime
# So this is taken straight out of the DjangoJSONEncoder implementation
# in master as of 7548aa8ffd46eb6e0f73730d1b2eb515ba581f95
class RichEncoder(object):
def default(self, o):
# See "Date Time String Format" in the ECMA-262 specification.
if isinstance(o, datetime.datetime):
r = o.isoformat()
if o.microsecond:
r = r[:23] + r[26:]
if r.endswith('+00:00'):
r = r[:-6] + 'Z'
return r
elif isinstance(o, datetime.date):
return o.isoformat()
elif isinstance(o, datetime.time):
if is_aware(o):
raise ValueError("JSON can't represent timezone-aware
times.")
r = o.isoformat()
if o.microsecond:
r = r[:12]
return r
elif isinstance(o, decimal.Decimal):
return str(o)
else:
return super(RichEncoder, self).default(o)
# this line is what would end up as DjangoJSONEncoder
# and also aliased as DateTimeAwareJSONEncoder
class stdlibJSONEncoder(RichEncoder, json.JSONEncoder): pass
# this would by necessity be a userland implementation, but is obviously
less
# code to copy-paste & maintain.
class simpleJSONEncoder(RichEncoder, simplejson.JSONEncoder): pass
data = {'decimal': decimal.Decimal('4.011'), 'datetime':
datetime.datetime.today()}
print "Naive:"
# naive, stdlib
try:
json.dumps(data)
except TypeError as e:
print e
# naive, simplejson
try:
simplejson.dumps(data)
except TypeError as e:
print e
print "\n\nEncoding from Django:"
print json.dumps(data, cls=stdlibJSONEncoder)
print simplejson.dumps(data, cls=simpleJSONEncoder)
# allow decimals to be strings
print simplejson.dumps(data, cls=simpleJSONEncoder, use_decimal=False)
class CustomJSONEncoder(object):
def default(self, o):
# contrived example ...
if hasattr(o, '__iter__'):
return tuple(o)
return super(CustomJSONEncoder, self).default(o)
# so you need to encode *other* things, but you want the date/decimal
handling
# both of these would obviously be userland implementations
class stdlibCustomJSONEncoder(CustomJSONEncoder, stdlibJSONEncoder): pass
class simpleCustomJSONEncoder(CustomJSONEncoder, simpleJSONEncoder): pass
# long-winded version.
class simpleCustomJSONEncoder2(CustomJSONEncoder, RichEncoder,
simplejson.JSONEncoder): pass
more_data = {'xrange': xrange(0, 10), 'datetime':
datetime.datetime.today()}
print "\n\nEncoding from Django, + extra stuff:"
print json.dumps(more_data, cls=stdlibCustomJSONEncoder)
print simplejson.dumps(more_data, cls=simpleCustomJSONEncoder)
# long-winded version, as above
print simplejson.dumps(more_data, cls=simpleCustomJSONEncoder2)
print "\n\nWithout our custom encoder:"
last_data = {'xrange': xrange(0, 10)}
try:
json.dumps(last_data)
except TypeError as e:
print e
try:
simplejson.dumps(last_data)
except TypeError as e:
print e
}}}
None of these exhibit the `namedtuple_as_object` issue presented in the
ticket itself, because their encoders match their expectations.
If `simplejson` is still buggy, 30-40 releases later, as the mentioned
ticket indicated, that's fine; that's for an end-user to deal with, or for
`simplejson` to handle in future releases, but as long as the contract for
a JSONEncoder instance's default is just accept `o` and return a
serializable version of it, I'd think the above would continue to work.
By separating the custom `default` object introspection out of the encoder
itself, it at least grants the possibility of users getting the best of
both Django's code and simplejson's, if that is their desire. The danger
of passing a `json.JSONEncoder` to `simplejson.dumps()` as that ticket
describes, is then circumventable, though left as an exercise to the user.
--
Ticket URL: <https://code.djangoproject.com/ticket/22821#comment:2>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/095.7899460eeeabc67a3b7e0beef516e726%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.