00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 """Tools for using Python's :mod:`json` module with BSON documents.
00016
00017 This module provides two methods: `object_hook` and `default`. These
00018 names are pretty terrible, but match the names used in Python's `json
00019 library <http://docs.python.org/library/json.html>`_. They allow for
00020 specialized encoding and decoding of BSON documents into `Mongo
00021 Extended JSON
00022 <http://www.mongodb.org/display/DOCS/Mongo+Extended+JSON>`_'s *Strict*
00023 mode. This lets you encode / decode BSON documents to JSON even when
00024 they use special BSON types.
00025
00026 Example usage (serialization)::
00027
00028 >>> json.dumps(..., default=json_util.default)
00029
00030 Example usage (deserialization)::
00031
00032 >>> json.loads(..., object_hook=json_util.object_hook)
00033
00034 Currently this does not handle special encoding and decoding for
00035 :class:`~bson.binary.Binary` and :class:`~bson.code.Code` instances.
00036
00037 .. versionchanged:: 1.9
00038 Handle :class:`uuid.UUID` instances, whenever possible.
00039
00040 .. versionchanged:: 1.8
00041 Handle timezone aware datetime instances on encode, decode to
00042 timezone aware datetime instances.
00043
00044 .. versionchanged:: 1.8
00045 Added support for encoding/decoding :class:`~bson.max_key.MaxKey`
00046 and :class:`~bson.min_key.MinKey`, and for encoding
00047 :class:`~bson.timestamp.Timestamp`.
00048
00049 .. versionchanged:: 1.2
00050 Added support for encoding/decoding datetimes and regular expressions.
00051 """
00052
00053 import calendar
00054 import datetime
00055 import re
00056 try:
00057 import uuid
00058 _use_uuid = True
00059 except ImportError:
00060 _use_uuid = False
00061
00062 from bson.dbref import DBRef
00063 from bson.max_key import MaxKey
00064 from bson.min_key import MinKey
00065 from bson.objectid import ObjectId
00066 from bson.timestamp import Timestamp
00067 from bson.tz_util import utc
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077 _RE_TYPE = type(re.compile("foo"))
00078
00079
00080 def object_hook(dct):
00081 if "$oid" in dct:
00082 return ObjectId(str(dct["$oid"]))
00083 if "$ref" in dct:
00084 return DBRef(dct["$ref"], dct["$id"], dct.get("$db", None))
00085 if "$date" in dct:
00086 return datetime.datetime.fromtimestamp(float(dct["$date"]) / 1000.0,
00087 utc)
00088 if "$regex" in dct:
00089 flags = 0
00090 if "i" in dct["$options"]:
00091 flags |= re.IGNORECASE
00092 if "m" in dct["$options"]:
00093 flags |= re.MULTILINE
00094 return re.compile(dct["$regex"], flags)
00095 if "$minKey" in dct:
00096 return MinKey()
00097 if "$maxKey" in dct:
00098 return MaxKey()
00099 if _use_uuid and "$uuid" in dct:
00100 return uuid.UUID(dct["$uuid"])
00101 return dct
00102
00103
00104 def default(obj):
00105 if isinstance(obj, ObjectId):
00106 return {"$oid": str(obj)}
00107 if isinstance(obj, DBRef):
00108 return obj.as_doc()
00109 if isinstance(obj, datetime.datetime):
00110
00111 if obj.utcoffset() is not None:
00112 obj = obj - obj.utcoffset()
00113 millis = int(calendar.timegm(obj.timetuple()) * 1000 +
00114 obj.microsecond / 1000)
00115 return {"$date": millis}
00116 if isinstance(obj, _RE_TYPE):
00117 flags = ""
00118 if obj.flags & re.IGNORECASE:
00119 flags += "i"
00120 if obj.flags & re.MULTILINE:
00121 flags += "m"
00122 return {"$regex": obj.pattern,
00123 "$options": flags}
00124 if isinstance(obj, MinKey):
00125 return {"$minKey": 1}
00126 if isinstance(obj, MaxKey):
00127 return {"$maxKey": 1}
00128 if isinstance(obj, Timestamp):
00129 return {"t": obj.time, "i": obj.inc}
00130 if _use_uuid and isinstance(obj, uuid.UUID):
00131 return {"$uuid": obj.hex}
00132 raise TypeError("%r is not JSON serializable" % obj)