Browse files

json_util fix format for binary type PYTHON-443

  • Loading branch information...
1 parent 0bf4d50 commit a31cac08896138abe6bce06647c94c8f56f480fc @rozza rozza committed Dec 5, 2012
Showing with 46 additions and 9 deletions.
  1. +10 −5 bson/json_util.py
  2. +36 −4 test/test_json_util.py
View
15 bson/json_util.py
@@ -32,14 +32,14 @@
... {'bar': {'hello': 'world'}},
... {'code': Code("function x() { return 1; }")},
... {'bin': Binary("\x00\x01\x02\x03\x04")}])
- '[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$scope": {}, "$code": "function x() { return 1; }"}}, {"bin": {"$type": 0, "$binary": "AAECAwQ=\\n"}}]'
+ '[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$scope": {}, "$code": "function x() { return 1; }"}}, {"bin": {"$type": "00", "$binary": "AAECAwQ="}}]'
Example usage (deserialization)::
.. doctest::
>>> from bson.json_util import loads
- >>> loads('[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$scope": {}, "$code": "function x() { return 1; }"}}, {"bin": {"$type": 0, "$binary": "AAECAwQ=\\n"}}]')
+ >>> loads('[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$scope": {}, "$code": "function x() { return 1; }"}}, {"bin": {"$type": "00", "$binary": "AAECAwQ="}}]')
[{u'foo': [1, 2]}, {u'bar': {u'hello': u'world'}}, {u'code': Code('function x() { return 1; }', {})}, {u'bin': Binary('\x00\x01\x02\x03\x04', 0)}]
Alternatively, you can manually pass the `default` to :func:`json.dumps`.
@@ -151,7 +151,12 @@ def object_hook(dct):
if "$maxKey" in dct:
return MaxKey()
if "$binary" in dct:
- return Binary(base64.b64decode(dct["$binary"].encode()), dct["$type"])
+ if isinstance(dct["$type"], int):
+ dct["$type"] = "%02x" % dct["$type"]
+ subtype = int(dct["$type"], 16)
+ if subtype >= 0xffffff80: # Handle mongoexport values
+ subtype = int(dct["$type"][6:], 16)
+ return Binary(base64.b64decode(dct["$binary"].encode()), subtype)
if "$code" in dct:
return Code(dct["$code"], dct.get("$scope"))
if bson.has_uuid() and "$uuid" in dct:
@@ -189,10 +194,10 @@ def default(obj):
return {'$code': "%s" % obj, '$scope': obj.scope}
if isinstance(obj, Binary):
return {'$binary': base64.b64encode(obj).decode(),
- '$type': obj.subtype}
+ '$type': "%02x" % obj.subtype}
if PY3 and isinstance(obj, binary_type):
return {'$binary': base64.b64encode(obj).decode(),
- '$type': 0}
+ '$type': "00"}
if bson.has_uuid() and isinstance(obj, bson.uuid.UUID):
return {"$uuid": obj.hex}
raise TypeError("%r is not JSON serializable" % obj)
View
40 test/test_json_util.py
@@ -26,7 +26,7 @@
import bson
from bson.py3compat import b
from bson import json_util
-from bson.binary import Binary, MD5_SUBTYPE
+from bson.binary import Binary, MD5_SUBTYPE, USER_DEFINED_SUBTYPE
from bson.code import Code
from bson.dbref import DBRef
from bson.max_key import MaxKey
@@ -101,10 +101,42 @@ def test_uuid(self):
'f47ac10b-58cc-4372-a567-0e02b2c3d479')})
def test_binary(self):
- self.round_trip({"bin": Binary(b("\x00\x01\x02\x03\x04"))})
- self.round_trip({
+ bin_type_dict = {"bin": Binary(b("\x00\x01\x02\x03\x04"))}
+ md5_type_dict = {
"md5": Binary(b(' n7\x18\xaf\t/\xd1\xd1/\x80\xca\xe7q\xcc\xac'),
- MD5_SUBTYPE)})
+ MD5_SUBTYPE)}
+ custom_type_dict = {"custom": Binary(b("hello"), USER_DEFINED_SUBTYPE)}
+
+ self.round_trip(bin_type_dict)
+ self.round_trip(md5_type_dict)
+ self.round_trip(custom_type_dict)
+
+ # PYTHON-443 ensure old type formats are supported
+ json_bin_dump = json_util.dumps(bin_type_dict)
+ self.assertTrue('"$type": "00"' in json_bin_dump)
+ self.assertEqual(bin_type_dict,
+ 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_type_dict,
+ json_util.loads('{"md5": {"$type": 5, "$binary":'
+ ' "IG43GK8JL9HRL4DK53HMrA=="}}'))
+
+ json_bin_dump = json_util.dumps(custom_type_dict)
+ self.assertTrue('"$type": "80"' in json_bin_dump)
+ self.assertEqual(custom_type_dict,
+ json_util.loads('{"custom": {"$type": 128, "$binary":'
+ ' "aGVsbG8="}}'))
+
+ # Handle mongoexport where subtype >= 128
+ self.assertEqual(128,
+ json_util.loads('{"custom": {"$type": "ffffff80", "$binary":'
+ ' "aGVsbG8="}}')['custom'].subtype)
+
+ self.assertEqual(255,
+ json_util.loads('{"custom": {"$type": "ffffffff", "$binary":'
+ ' "aGVsbG8="}}')['custom'].subtype)
def test_code(self):
self.round_trip({"code": Code("function x() { return 1; }")})

0 comments on commit a31cac0

Please sign in to comment.