So, I have the following DICT in python:
{'documentType': {'documentTypeId': 'XXXXXXXXXXXXXX',
'documentTypeName': 'XXXXXXXXXXXXXXXXXXXX',
'documentTypeVersion': 1},
'tags': [{'boundingBoxCoOrdinates': {'x1': 440.66135,
'y1': 894.0904,
'x2': 468.74966,
'y2': 917.39105},
'digitalPdfTagDetails': {'startPage': 0,
'endPage': 0,
'startCharIndex': 0,
'endCharIndex': 0},
'editedDate': 'SOMEDATET12:47:36Z',
'contains': True,
'tagId': 0,
'tagName': 'SOMETAG',
'type': 'SOMETYPE',
'color': '#SOMECOLOR',
'extractedData': '',
'editedData': '',
'editedBy': 'SOMEPERSON'}],
'uploadedFileId': 'SOMEID',
'imageHeight': 55,
'imageWidth': 55,
'Engine': 'SOMEPROCESSINGENGINE',
'fileName': 'SOMEFILENAME.jpg'}
When I pass it for a POST api using Requests library in the JSON argument, I am greeted with
TypeError: Object of type 'float32' is not JSON serializable
I suspect, this is because of the values inside the 'boundingBoxCoOrdinates' which are numpy.float32 type. If I convert them to numpy.float64 using astype(numpy.float64), then it seems to be working fine.
How do I convert all such values in this DICT so as to JSON serializable error does not persist?
EDIT: There can be multiple such dictionaries inside 'tags' LIST
You need to use a custom encoder for all data types that don't fit in the standard range of data types.
Custom encoders are created by subclassing the json.JSONEncoder class:
import json
import numpy as np
class NumpyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, np.float32):
return "...an appropriate representation of the item that the recipient understands..."
# add more cases for NumPy data types as you need them
# all other cases get default encoding
return json.JSONEncoder.default(self, obj)
json_str = json.dumps(your_data_structure, cls=NumpyEncoder)
As stated into the requests documentation here, you can use json parameter to let requests manage the encoding of the dict as this:
url = 'https://api.github.com/some/endpoint'
payload = {'some': 'data'}
r = requests.post(url, json=payload)
But, since you're getting that error, you can try to encode the dict yourself and send it via the data parameter like this:
import json
url = 'https://api.github.com/some/endpoint'
payload = {'some': 'data'}
r = requests.post(url, data=json.dumps(payload))
You can use the default argument in the json call to handle conversion of numpy arrays:
import numpy as np
import json
d = {"floats": np.ones(10) / np.arange(1, 11)}
def handler(x):
if isinstance(x, np.ndarray):
return [y.item() for y in x]
else:
return x
json_str = json.dumps(d, default=handler)
I have a basic dict as follows:
sample = {}
sample['title'] = "String"
sample['somedate'] = somedatetimehere
When I try to do jsonify(sample) I get:
TypeError: datetime.datetime(2012, 8, 8, 21, 46, 24, 862000) is not JSON serializable
What can I do such that my dictionary sample can overcome the error above?
Note: Though it may not be relevant, the dictionaries are generated from the retrieval of records out of mongodb where when I print out str(sample['somedate']), the output is 2012-08-08 21:46:24.862000.
My quick & dirty JSON dump that eats dates and everything:
json.dumps(my_dictionary, indent=4, sort_keys=True, default=str)
default is a function applied to objects that aren't serializable.
In this case it's str, so it just converts everything it doesn't know to strings. Which is great for serialization but not so great when deserializing (hence the "quick & dirty") as anything might have been string-ified without warning, e.g. a function or numpy array.
Building on other answers, a simple solution based on a specific serializer that just converts datetime.datetime and datetime.date objects to strings.
from datetime import date, datetime
def json_serial(obj):
"""JSON serializer for objects not serializable by default json code"""
if isinstance(obj, (datetime, date)):
return obj.isoformat()
raise TypeError ("Type %s not serializable" % type(obj))
As seen, the code just checks to find out if object is of class datetime.datetime or datetime.date, and then uses .isoformat() to produce a serialized version of it, according to ISO 8601 format, YYYY-MM-DDTHH:MM:SS (which is easily decoded by JavaScript). If more complex serialized representations are sought, other code could be used instead of str() (see other answers to this question for examples). The code ends by raising an exception, to deal with the case it is called with a non-serializable type.
This json_serial function can be used as follows:
from datetime import datetime
from json import dumps
print dumps(datetime.now(), default=json_serial)
The details about how the default parameter to json.dumps works can be found in Section Basic Usage of the json module documentation.
Updated for 2018
The original answer accommodated the way MongoDB "date" fields were represented as:
{"$date": 1506816000000}
If you want a generic Python solution for serializing datetime to json, check out #jjmontes' answer for a quick solution which requires no dependencies.
As you are using mongoengine (per comments) and pymongo is a dependency, pymongo has built-in utilities to help with json serialization:
http://api.mongodb.org/python/1.10.1/api/bson/json_util.html
Example usage (serialization):
from bson import json_util
import json
json.dumps(anObject, default=json_util.default)
Example usage (deserialization):
json.loads(aJsonString, object_hook=json_util.object_hook)
Django
Django provides a native DjangoJSONEncoder serializer that deals with this kind of properly.
See https://docs.djangoproject.com/en/dev/topics/serialization/#djangojsonencoder
from django.core.serializers.json import DjangoJSONEncoder
return json.dumps(
item,
sort_keys=True,
indent=1,
cls=DjangoJSONEncoder
)
One difference I've noticed between DjangoJSONEncoder and using a custom default like this:
import datetime
import json
def default(o):
if isinstance(o, (datetime.date, datetime.datetime)):
return o.isoformat()
return json.dumps(
item,
sort_keys=True,
indent=1,
default=default
)
Is that Django strips a bit of the data:
"last_login": "2018-08-03T10:51:42.990", # DjangoJSONEncoder
"last_login": "2018-08-03T10:51:42.990239", # default
So, you may need to be careful about that in some cases.
I have just encountered this problem and my solution is to subclass json.JSONEncoder:
from datetime import datetime
import json
class DateTimeEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, datetime):
return o.isoformat()
return json.JSONEncoder.default(self, o)
In your call do something like: json.dumps(yourobj, cls=DateTimeEncoder) The .isoformat() I got from one of the answers above.
Convert the date to a string
sample['somedate'] = str( datetime.utcnow() )
For others who do not need or want to use the pymongo library for this.. you can achieve datetime JSON conversion easily with this small snippet:
def default(obj):
"""Default JSON serializer."""
import calendar, datetime
if isinstance(obj, datetime.datetime):
if obj.utcoffset() is not None:
obj = obj - obj.utcoffset()
millis = int(
calendar.timegm(obj.timetuple()) * 1000 +
obj.microsecond / 1000
)
return millis
raise TypeError('Not sure how to serialize %s' % (obj,))
Then use it like so:
import datetime, json
print json.dumps(datetime.datetime.now(), default=default)
output:
'1365091796124'
Here is my solution:
import json
class DatetimeEncoder(json.JSONEncoder):
def default(self, obj):
try:
return super().default(obj)
except TypeError:
return str(obj)
Then you can use it like that:
json.dumps(dictionnary, cls=DatetimeEncoder)
if you are using python3.7, then the best solution is using
datetime.isoformat() and
datetime.fromisoformat(); they work with both naive and
aware datetime objects:
#!/usr/bin/env python3.7
from datetime import datetime
from datetime import timezone
from datetime import timedelta
import json
def default(obj):
if isinstance(obj, datetime):
return { '_isoformat': obj.isoformat() }
raise TypeError('...')
def object_hook(obj):
_isoformat = obj.get('_isoformat')
if _isoformat is not None:
return datetime.fromisoformat(_isoformat)
return obj
if __name__ == '__main__':
#d = { 'now': datetime(2000, 1, 1) }
d = { 'now': datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=-8))) }
s = json.dumps(d, default=default)
print(s)
print(d == json.loads(s, object_hook=object_hook))
output:
{"now": {"_isoformat": "2000-01-01T00:00:00-08:00"}}
True
if you are using python3.6 or below, and you only care about the time value (not
the timezone), then you can use datetime.timestamp() and
datetime.fromtimestamp() instead;
if you are using python3.6 or below, and you do care about the timezone, then
you can get it via datetime.tzinfo, but you have to serialize this field
by yourself; the easiest way to do this is to add another field _tzinfo in the
serialized object;
finally, beware of precisions in all these examples;
You should apply .strftime() method on .datetime.now() method to make it as a serializable method.
Here's an example:
from datetime import datetime
time_dict = {'time': datetime.now().strftime('%Y-%m-%dT%H:%M:%S')}
sample_dict = {'a': 1, 'b': 2}
sample_dict.update(time_dict)
sample_dict
Output:
Out[0]: {'a': 1, 'b': 2, 'time': '2017-10-31T15:16:30'}
[UPDATE]:
In Python3.7 and later, you can simply use the .isoformat() method:
from datetime import datetime
datetime.now().isoformat()
I have an application with a similar issue; my approach was to JSONize the datetime value as a 6-item list (year, month, day, hour, minutes, seconds); you could go to microseconds as a 7-item list, but I had no need to:
class DateTimeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
encoded_object = list(obj.timetuple())[0:6]
else:
encoded_object =json.JSONEncoder.default(self, obj)
return encoded_object
sample = {}
sample['title'] = "String"
sample['somedate'] = datetime.datetime.now()
print sample
print json.dumps(sample, cls=DateTimeEncoder)
produces:
{'somedate': datetime.datetime(2013, 8, 1, 16, 22, 45, 890000), 'title': 'String'}
{"somedate": [2013, 8, 1, 16, 22, 45], "title": "String"}
The json.dumps method can accept an optional parameter called default which is expected to be a function. Every time JSON tries to convert a value it does not know how to convert it will call the function we passed to it. The function will receive the object in question, and it is expected to return the JSON representation of the object.
def myconverter(o):
if isinstance(o, datetime.datetime):
return o.__str__()
print(json.dumps(d, default = myconverter))
My solution (with less verbosity, I think):
def default(o):
if type(o) is datetime.date or type(o) is datetime.datetime:
return o.isoformat()
def jsondumps(o):
return json.dumps(o, default=default)
Then use jsondumps instead of json.dumps. It will print:
>>> jsondumps({'today': datetime.date.today()})
'{"today": "2013-07-30"}'
I you want, later you can add other special cases to this with a simple twist of the default method. Example:
def default(o):
if type(o) is datetime.date or type(o) is datetime.datetime:
return o.isoformat()
if type(o) is decimal.Decimal:
return float(o)
This Q repeats time and time again - a simple way to patch the json module such that serialization would support datetime.
import json
import datetime
json.JSONEncoder.default = lambda self,obj: (obj.isoformat() if isinstance(obj, datetime.datetime) else None)
Than use json serialization as you always do - this time with datetime being serialized as isoformat.
json.dumps({'created':datetime.datetime.now()})
Resulting in: '{"created": "2015-08-26T14:21:31.853855"}'
See more details and some words of caution at:
StackOverflow: JSON datetime between Python and JavaScript
You have to supply a custom encoder class with the cls parameter of json.dumps. To quote from the docs:
>>> import json
>>> class ComplexEncoder(json.JSONEncoder):
... def default(self, obj):
... if isinstance(obj, complex):
... return [obj.real, obj.imag]
... return json.JSONEncoder.default(self, obj)
...
>>> dumps(2 + 1j, cls=ComplexEncoder)
'[2.0, 1.0]'
>>> ComplexEncoder().encode(2 + 1j)
'[2.0, 1.0]'
>>> list(ComplexEncoder().iterencode(2 + 1j))
['[', '2.0', ', ', '1.0', ']']
This uses complex numbers as the example, but you can just as easily create a class to encode dates (except I think JSON is a little fuzzy about dates)
Here is a simple solution to over come "datetime not JSON serializable"
problem.
enco = lambda obj: (
obj.isoformat()
if isinstance(obj, datetime.datetime)
or isinstance(obj, datetime.date)
else None
)
json.dumps({'date': datetime.datetime.now()}, default=enco)
Output:-> {"date": "2015-12-16T04:48:20.024609"}
The simplest way to do this is to change the part of the dict that is in datetime format to isoformat. That value will effectively be a string in isoformat which json is ok with.
v_dict = version.dict()
v_dict['created_at'] = v_dict['created_at'].isoformat()
Actually it is quite simple.
If you need to often serialize dates, then work with them as strings. You can easily convert them back as datetime objects if needed.
If you need to work mostly as datetime objects, then convert them as strings before serializing.
import json, datetime
date = str(datetime.datetime.now())
print(json.dumps(date))
"2018-12-01 15:44:34.409085"
print(type(date))
<class 'str'>
datetime_obj = datetime.datetime.strptime(date, '%Y-%m-%d %H:%M:%S.%f')
print(datetime_obj)
2018-12-01 15:44:34.409085
print(type(datetime_obj))
<class 'datetime.datetime'>
As you can see, the output is the same in both cases. Only the type is different.
Try this one with an example to parse it:
#!/usr/bin/env python
import datetime
import json
import dateutil.parser # pip install python-dateutil
class JSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
return obj.isoformat()
return super(JSONEncoder, self).default(obj)
def test():
dts = [
datetime.datetime.now(),
datetime.datetime.now(datetime.timezone(-datetime.timedelta(hours=4))),
datetime.datetime.utcnow(),
datetime.datetime.now(datetime.timezone.utc),
]
for dt in dts:
dt_isoformat = json.loads(json.dumps(dt, cls=JSONEncoder))
dt_parsed = dateutil.parser.parse(dt_isoformat)
assert dt == dt_parsed
print(f'{dt}, {dt_isoformat}, {dt_parsed}')
# 2018-07-22 02:22:42.910637, 2018-07-22T02:22:42.910637, 2018-07-22 02:22:42.910637
# 2018-07-22 02:22:42.910643-04:00, 2018-07-22T02:22:42.910643-04:00, 2018-07-22 02:22:42.910643-04:00
# 2018-07-22 06:22:42.910645, 2018-07-22T06:22:42.910645, 2018-07-22 06:22:42.910645
# 2018-07-22 06:22:42.910646+00:00, 2018-07-22T06:22:42.910646+00:00, 2018-07-22 06:22:42.910646+00:00
if __name__ == '__main__':
test()
If you are working with django models you can directly pass encoder=DjangoJSONEncoder to the field constructor. It will work like a charm.
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models
from django.utils.timezone import now
class Activity(models.Model):
diff = models.JSONField(null=True, blank=True, encoder=DjangoJSONEncoder)
diff = {
"a": 1,
"b": "BB",
"c": now()
}
Activity.objects.create(diff=diff)
Generally there are several ways to serialize datetimes, like:
ISO string, short and can include timezone info, e.g. #jgbarah's answer
Timestamp (timezone data is lost), e.g. #JayTaylor's answer
Dictionary of properties (including timezone).
If you're okay with the last way, the json_tricks package handles dates, times and datetimes including timezones.
from datetime import datetime
from json_tricks import dumps
foo = {'title': 'String', 'datetime': datetime(2012, 8, 8, 21, 46, 24, 862000)}
dumps(foo)
which gives:
{"title": "String", "datetime": {"__datetime__": null, "year": 2012, "month": 8, "day": 8, "hour": 21, "minute": 46, "second": 24, "microsecond": 862000}}
So all you need to do is
`pip install json_tricks`
and then import from json_tricks instead of json.
The advantage of not storing it as a single string, int or float comes when decoding: if you encounter just a string or especially int or float, you need to know something about the data to know if it's a datetime. As a dict, you can store metadata so it can be decoded automatically, which is what json_tricks does for you. It's also easily editable for humans.
Disclaimer: it's made by me. Because I had the same problem.
I usually use orjson. Not only because of its tremendous performance, but also for its great (RFC-3339 compliant) support of datetime:
import orjson # via pip3 install orjson
from datetime import datetime
data = {"created_at": datetime(2022, 3, 1)}
orjson.dumps(data) # returns b'{"created_at":"2022-03-01T00:00:00"}'
If you would like to use datetime.datetime objects without a tzinfo as UTC you can add the related option:
orjson.dumps(data, option=orjson.OPT_NAIVE_UTC) # returns b'{"created_at":"2022-03-01T00:00:00+00:00"}'
If you are using the result in a view be sure to return a proper response. According to the API, jsonify does the following:
Creates a Response with the JSON representation of the given arguments
with an application/json mimetype.
To mimic this behavior with json.dumps you have to add a few extra lines of code.
response = make_response(dumps(sample, cls=CustomEncoder))
response.headers['Content-Type'] = 'application/json'
response.headers['mimetype'] = 'application/json'
return response
You should also return a dict to fully replicate jsonify's response. So, the entire file will look like this
from flask import make_response
from json import JSONEncoder, dumps
class CustomEncoder(JSONEncoder):
def default(self, obj):
if set(['quantize', 'year']).intersection(dir(obj)):
return str(obj)
elif hasattr(obj, 'next'):
return list(obj)
return JSONEncoder.default(self, obj)
#app.route('/get_reps/', methods=['GET'])
def get_reps():
sample = ['some text', <datetime object>, 123]
response = make_response(dumps({'result': sample}, cls=CustomEncoder))
response.headers['Content-Type'] = 'application/json'
response.headers['mimetype'] = 'application/json'
return response
My solution ...
from datetime import datetime
import json
from pytz import timezone
import pytz
def json_dt_serializer(obj):
"""JSON serializer, by macm.
"""
rsp = dict()
if isinstance(obj, datetime):
rsp['day'] = obj.day
rsp['hour'] = obj.hour
rsp['microsecond'] = obj.microsecond
rsp['minute'] = obj.minute
rsp['month'] = obj.month
rsp['second'] = obj.second
rsp['year'] = obj.year
rsp['tzinfo'] = str(obj.tzinfo)
return rsp
raise TypeError("Type not serializable")
def json_dt_deserialize(obj):
"""JSON deserialize from json_dt_serializer, by macm.
"""
if isinstance(obj, str):
obj = json.loads(obj)
tzone = timezone(obj['tzinfo'])
tmp_dt = datetime(obj['year'],
obj['month'],
obj['day'],
hour=obj['hour'],
minute=obj['minute'],
second=obj['second'],
microsecond=obj['microsecond'])
loc_dt = tzone.localize(tmp_dt)
deserialize = loc_dt.astimezone(tzone)
return deserialize
Ok, now some tests.
# Tests
now = datetime.now(pytz.utc)
# Using this solution
rsp = json_dt_serializer(now)
tmp = json_dt_deserialize(rsp)
assert tmp == now
assert isinstance(tmp, datetime) == True
assert isinstance(now, datetime) == True
# using default from json.dumps
tmp = json.dumps(datetime.now(pytz.utc), default=json_dt_serializer)
rsp = json_dt_deserialize(tmp)
assert isinstance(rsp, datetime) == True
# Lets try another timezone
eastern = timezone('US/Eastern')
now = datetime.now(eastern)
rsp = json_dt_serializer(now)
tmp = json_dt_deserialize(rsp)
print(tmp)
# 2015-10-22 09:18:33.169302-04:00
print(now)
# 2015-10-22 09:18:33.169302-04:00
# Wow, Works!
assert tmp == now
Here is my full solution for converting datetime to JSON and back..
import calendar, datetime, json
def outputJSON(obj):
"""Default JSON serializer."""
if isinstance(obj, datetime.datetime):
if obj.utcoffset() is not None:
obj = obj - obj.utcoffset()
return obj.strftime('%Y-%m-%d %H:%M:%S.%f')
return str(obj)
def inputJSON(obj):
newDic = {}
for key in obj:
try:
if float(key) == int(float(key)):
newKey = int(key)
else:
newKey = float(key)
newDic[newKey] = obj[key]
continue
except ValueError:
pass
try:
newDic[str(key)] = datetime.datetime.strptime(obj[key], '%Y-%m-%d %H:%M:%S.%f')
continue
except TypeError:
pass
newDic[str(key)] = obj[key]
return newDic
x = {'Date': datetime.datetime.utcnow(), 34: 89.9, 12.3: 90, 45: 67, 'Extra': 6}
print x
with open('my_dict.json', 'w') as fp:
json.dump(x, fp, default=outputJSON)
with open('my_dict.json') as f:
my_dict = json.load(f, object_hook=inputJSON)
print my_dict
Output
{'Date': datetime.datetime(2013, 11, 8, 2, 30, 56, 479727), 34: 89.9, 45: 67, 12.3: 90, 'Extra': 6}
{'Date': datetime.datetime(2013, 11, 8, 2, 30, 56, 479727), 34: 89.9, 45: 67, 12.3: 90, 'Extra': 6}
JSON File
{"Date": "2013-11-08 02:30:56.479727", "34": 89.9, "45": 67, "12.3": 90, "Extra": 6}
This has enabled me to import and export strings, ints, floats and datetime objects.
It shouldn't be to hard to extend for other types.
Convert the date to string
date = str(datetime.datetime(somedatetimehere))
As per the #jjmontes answer, I have used the following approach.
For flask and flask-restful users
# get json string
jsonStr = json.dumps(my_dictionary, indent=1, sort_keys=True, default=str)
# then covert json string to json object
return json.loads(jsonStr)
I got the same error message while writing the serialize decorator inside a Class with sqlalchemy. So instead of :
Class Puppy(Base):
...
#property
def serialize(self):
return { 'id':self.id,
'date_birth':self.date_birth,
...
}
I simply borrowed jgbarah's idea of using isoformat() and appended the original value with isoformat(), so that it now looks like:
...
'date_birth':self.date_birth.isoformat(),
...
A quick fix if you want your own formatting
for key,val in sample.items():
if isinstance(val, datetime):
sample[key] = '{:%Y-%m-%d %H:%M:%S}'.format(val) #you can add different formating here
json.dumps(sample)
If you are on both sides of the communication you can use repr() and eval() functions along with json.
import datetime, json
dt = datetime.datetime.now()
print("This is now: {}".format(dt))
dt1 = json.dumps(repr(dt))
print("This is serialised: {}".format(dt1))
dt2 = json.loads(dt1)
print("This is loaded back from json: {}".format(dt2))
dt3 = eval(dt2)
print("This is the same object as we started: {}".format(dt3))
print("Check if they are equal: {}".format(dt == dt3))
You shouldn't import datetime as
from datetime import datetime
since eval will complain. Or you can pass datetime as a parameter to eval. In any case this should work.
I had encountered same problem when externalizing django model object to dump as JSON.
Here is how you can solve it.
def externalize(model_obj):
keys = model_obj._meta.get_all_field_names()
data = {}
for key in keys:
if key == 'date_time':
date_time_obj = getattr(model_obj, key)
data[key] = date_time_obj.strftime("%A %d. %B %Y")
else:
data[key] = getattr(model_obj, key)
return data
When I tried to encode the query objects I get the following error:
File "C:\Program File\Python27\lib\json\encoder.py", line 264, in iterencode
return _iterencode(o, 0)
File "C:\Program File\Python27\lib\json\encoder.py", line 178, in default
raise TypeError(repr(o) + " is not JSON serializable")
TypeError: ActivitySummaries(key=Key('ActivitySummaries', 634), activated_users=0, broker_approved=0, broker_registered=0, broker_searched=1, closed_deals=0, company_registered=0, company_searched=4, deal_appoved=0, investor_approved=0, investor_registered=0, investor_searched=3, registered_users=0, timestamp=datetime.datetime(2013, 4, 8, 20, 41, 47, 574000), watchlisting=0) is not JSON serializable
jquery:
$.ajax({
data: someData,
url: someUrl,
type: 'POST',
dataType: 'json',
success: function(data)
{
alert("Success");
},
error : function(request, status, thrownError){
alert("Error");
return;
}
});
Handler:
search_pattern = roledb.ActivitySummaries.searchPatterns(start_date, end_date)
self.response.write(json.dumps(search_pattern))
roledb.py
class ActivitySummaries(ndb.Model):
def searchPatterns(cls, start_date, end_date):
activities = cls.query()
results = []
for activity in activities:
if ( activity.timestamp >= start_date and activity.timestamp <= end_date ):
results.append(activity)
return results
I'm new to Google App Engine and I have no idea why it cannot be serializable using JSON.
Any inputs will be heartily appreciated.
You can serialize only "simple" data types in Python, like dictionaries, arrays etc. So you should serialize not the query object, but the result of this query—I guess it'll be an array.
Another solution is to subclass JSONEncoder to handle arbitrary values, like the snippet I did for DateTime:
import datetime
from json import JSONEncoder
class DateEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.date):
return obj.isoformat()
return JSONEncoder.default(self, obj)
And to use it specify cls=DateEncoder:
json.dumps(data, cls=DateEncoder)
You can serialize pretty much anything you want to JSON but beyond simple structures you will need to do it all yourself. And write your own unpickers.
However in your case I doubt it makes any sense to serialize a Query object. What will you do with it at the other end ? Javascript can't do anything with it, and if you just want to pass query parameters around then just pass those around rather than a Query object.
Maybe you actually want to serialize the result set from the query and pass that back to the client. In that case do a fetch, or use a cursor to retrieve chunks of results.
The ndb.Model base class has a to_dict() method, which is what you would normally call and you would then serialize the dictionary.
result = json.dumps([dumps(i.to_dict()) for i in query.fetch(100))
or even better use the map method of query (there is even a map_async)
def dump(obj):
return json.dumps(obj.to_dict())
result = query.map(dump)
See https://developers.google.com/appengine/docs/python/ndb/queries#map
You will need to combine this with the other answer, to provide a custom encoder for dates and any other non simple data type.
I found that the following solution worked for serializing ndb.Model objects which contain ndb.DateTimeProperty as children.
from datetime import datetime
import json
from google.appengine.ext import ndb
__author__ = 'achuinard'
class GaeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return int(obj.strftime('%s'))
elif isinstance(obj, ndb.Model):
return obj.to_dict()
else:
return json.JSONEncoder.default(self, obj)
def serialize(object_to_serialize):
return json.dumps(object_to_serialize, cls=GaeEncoder)