Preserve order in json_util.dumps, PYTHON-602.
This commit is contained in:
parent
4b5ba70724
commit
51deead4ff
@ -83,7 +83,7 @@ except ImportError:
|
||||
json_lib = False
|
||||
|
||||
import bson
|
||||
from bson import EPOCH_AWARE, RE_TYPE
|
||||
from bson import EPOCH_AWARE, RE_TYPE, SON
|
||||
from bson.binary import Binary
|
||||
from bson.code import Code
|
||||
from bson.dbref import DBRef
|
||||
@ -111,6 +111,10 @@ def dumps(obj, *args, **kwargs):
|
||||
|
||||
Recursive function that handles all BSON types including
|
||||
:class:`~bson.binary.Binary` and :class:`~bson.code.Code`.
|
||||
|
||||
.. versionchanged:: 2.7
|
||||
Preserves order when rendering SON, Timestamp, Code, Binary, and DBRef
|
||||
instances.
|
||||
"""
|
||||
if not json_lib:
|
||||
raise Exception("No json library available")
|
||||
@ -143,7 +147,7 @@ def _json_convert(obj):
|
||||
converted into json.
|
||||
"""
|
||||
if hasattr(obj, 'iteritems') or hasattr(obj, 'items'): # PY3 support
|
||||
return dict(((k, _json_convert(v)) for k, v in obj.iteritems()))
|
||||
return SON(((k, _json_convert(v)) for k, v in obj.iteritems()))
|
||||
elif hasattr(obj, '__iter__') and not isinstance(obj, string_types):
|
||||
return list((_json_convert(v) for v in obj))
|
||||
try:
|
||||
@ -214,22 +218,23 @@ def default(obj):
|
||||
flags += "u"
|
||||
if obj.flags & re.VERBOSE:
|
||||
flags += "x"
|
||||
return {"$regex": obj.pattern,
|
||||
"$options": flags}
|
||||
return SON([("$regex", obj.pattern), ("$options", flags)])
|
||||
if isinstance(obj, MinKey):
|
||||
return {"$minKey": 1}
|
||||
if isinstance(obj, MaxKey):
|
||||
return {"$maxKey": 1}
|
||||
if isinstance(obj, Timestamp):
|
||||
return {"t": obj.time, "i": obj.inc}
|
||||
return SON([("t", obj.time), ("i", obj.inc)])
|
||||
if isinstance(obj, Code):
|
||||
return {'$code': "%s" % obj, '$scope': obj.scope}
|
||||
return SON([('$code', "%s" % obj), ('$scope', obj.scope)])
|
||||
if isinstance(obj, Binary):
|
||||
return {'$binary': base64.b64encode(obj).decode(),
|
||||
'$type': "%02x" % obj.subtype}
|
||||
return SON([
|
||||
('$binary', base64.b64encode(obj).decode()),
|
||||
('$type', "%02x" % obj.subtype)])
|
||||
if PY3 and isinstance(obj, binary_type):
|
||||
return {'$binary': base64.b64encode(obj).decode(),
|
||||
'$type': "00"}
|
||||
return SON([
|
||||
('$binary', base64.b64encode(obj).decode()),
|
||||
('$type', "00")])
|
||||
if bson.has_uuid() and isinstance(obj, bson.uuid.UUID):
|
||||
return {"$uuid": obj.hex}
|
||||
raise TypeError("%r is not JSON serializable" % obj)
|
||||
|
||||
@ -69,7 +69,11 @@ class TestJsonUtil(unittest.TestCase):
|
||||
self.round_trip({"ref": DBRef("foo", 5)})
|
||||
self.round_trip({"ref": DBRef("foo", 5, "db")})
|
||||
self.round_trip({"ref": DBRef("foo", ObjectId())})
|
||||
self.round_trip({"ref": DBRef("foo", ObjectId(), "db")})
|
||||
|
||||
# Check order.
|
||||
self.assertEqual(
|
||||
'{"$ref": "collection", "$id": 1, "$db": "db"}',
|
||||
json_util.dumps(DBRef('collection', 1, 'db')))
|
||||
|
||||
def test_datetime(self):
|
||||
# only millis, not micros
|
||||
@ -126,6 +130,15 @@ class TestJsonUtil(unittest.TestCase):
|
||||
'{"r": {"$regex": ".*", "$options": "ilm"}}',
|
||||
compile_re=False)['r'])
|
||||
|
||||
# Check order.
|
||||
self.assertEqual(
|
||||
'{"$regex": ".*", "$options": "mx"}',
|
||||
json_util.dumps(Regex('.*', re.M | re.X)))
|
||||
|
||||
self.assertEqual(
|
||||
'{"$regex": ".*", "$options": "mx"}',
|
||||
json_util.dumps(re.compile('.*', re.M | re.X)))
|
||||
|
||||
def test_minkey(self):
|
||||
self.round_trip({"m": MinKey()})
|
||||
|
||||
@ -135,6 +148,7 @@ class TestJsonUtil(unittest.TestCase):
|
||||
def test_timestamp(self):
|
||||
res = json_util.json.dumps({"ts": Timestamp(4, 13)},
|
||||
default=json_util.default)
|
||||
self.assertEqual('{"ts": {"t": 4, "i": 13}}', res)
|
||||
dct = json_util.json.loads(res)
|
||||
self.assertEqual(dct['ts']['t'], 4)
|
||||
self.assertEqual(dct['ts']['i'], 13)
|
||||
@ -164,7 +178,10 @@ class TestJsonUtil(unittest.TestCase):
|
||||
json_util.loads('{"bin": {"$type": 0, "$binary": "AAECAwQ="}}'))
|
||||
|
||||
json_bin_dump = json_util.dumps(md5_type_dict)
|
||||
self.assertTrue('"$type": "05"' in json_bin_dump)
|
||||
self.assertEqual(
|
||||
'{"md5": {"$binary": "IG43GK8JL9HRL4DK53HMrA==", "$type": "05"}}',
|
||||
json_bin_dump)
|
||||
|
||||
self.assertEqual(md5_type_dict,
|
||||
json_util.loads('{"md5": {"$type": 5, "$binary":'
|
||||
' "IG43GK8JL9HRL4DK53HMrA=="}}'))
|
||||
@ -186,7 +203,13 @@ class TestJsonUtil(unittest.TestCase):
|
||||
|
||||
def test_code(self):
|
||||
self.round_trip({"code": Code("function x() { return 1; }")})
|
||||
self.round_trip({"code": Code("function y() { return z; }", z=2)})
|
||||
|
||||
code = Code("return z", z=2)
|
||||
res = json_util.dumps(code)
|
||||
self.assertEqual(code, json_util.loads(res))
|
||||
|
||||
# Check order.
|
||||
self.assertEqual('{"$code": "return z", "$scope": {"z": 2}}', res)
|
||||
|
||||
def test_cursor(self):
|
||||
db = self.db
|
||||
|
||||
Loading…
Reference in New Issue
Block a user