|
30 | 30 |
|
31 | 31 | >>> json.loads(..., object_hook=json_util.object_hook) |
32 | 32 |
|
33 | | -Currently this only handles special encoding and decoding for |
34 | | -:class:`~pymongo.objectid.ObjectId` and :class:`~pymongo.dbref.DBRef` |
| 33 | +Currently this does not handle special encoding and decoding for |
| 34 | +:class:`~pymongo.binary.Binary` and :class:`~pymongo.code.Code` |
35 | 35 | instances. |
| 36 | +
|
| 37 | +.. versionchanged:: 1.1.2+ |
| 38 | + Added support for encoding/decoding datetimes and regular expressions. |
36 | 39 | """ |
37 | 40 |
|
| 41 | +import datetime |
| 42 | +import calendar |
| 43 | +import re |
| 44 | + |
38 | 45 | from objectid import ObjectId |
39 | 46 | from dbref import DBRef |
40 | 47 |
|
41 | | -# TODO support other types, like Binary, Code, datetime & regex |
| 48 | +# TODO support Binary and Code |
42 | 49 | # Binary and Code are tricky because they subclass str so json thinks it can |
43 | 50 | # handle them. Not sure what the proper way to get around this is... |
| 51 | +# |
| 52 | +# One option is to just add some other method that users need to call _before_ |
| 53 | +# calling json.dumps or json.loads. That is pretty terrible though... |
| 54 | + |
| 55 | +# TODO share this with bson.py? |
| 56 | +_RE_TYPE = type(re.compile("foo")) |
44 | 57 |
|
45 | 58 | def object_hook(dct): |
46 | 59 | if "$oid" in dct: |
47 | 60 | return ObjectId(str(dct["$oid"])) |
48 | 61 | if "$ref" in dct: |
49 | 62 | return DBRef(dct["$ref"], dct["$id"], dct.get("$db", None)) |
| 63 | + if "$date" in dct: |
| 64 | + return datetime.datetime.utcfromtimestamp(float(dct["$date"]) / 1000.0) |
| 65 | + if "$regex" in dct: |
| 66 | + flags = 0 |
| 67 | + if "i" in dct["$options"]: |
| 68 | + flags |= re.IGNORECASE |
| 69 | + if "m" in dct["$options"]: |
| 70 | + flags |= re.MULTILINE |
| 71 | + return re.compile(dct["$regex"], flags) |
50 | 72 | return dct |
51 | 73 |
|
52 | 74 | def default(obj): |
53 | 75 | if isinstance(obj, ObjectId): |
54 | 76 | return {"$oid": str(obj)} |
55 | 77 | if isinstance(obj, DBRef): |
56 | 78 | return obj.as_doc() |
| 79 | + if isinstance(obj, datetime.datetime): |
| 80 | + # TODO share this code w/ bson.py? |
| 81 | + millis = int(calendar.timegm(obj.timetuple()) * 1000 + |
| 82 | + obj.microsecond / 1000) |
| 83 | + return {"$date": str(millis)} |
| 84 | + if isinstance(obj, _RE_TYPE): |
| 85 | + flags = "" |
| 86 | + if obj.flags & re.IGNORECASE: |
| 87 | + flags += "i" |
| 88 | + if obj.flags & re.MULTILINE: |
| 89 | + flags += "m" |
| 90 | + return {"$regex": obj.pattern, |
| 91 | + "$options": flags} |
57 | 92 | raise TypeError("%r is not JSON serializable" % obj) |
0 commit comments