Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit 8201741

Browse files
committed
Add custom JSON encoder/decoder option to Document constructor
1 parent fa1cf06 commit 8201741

3 files changed

Lines changed: 51 additions & 3 deletions

File tree

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Unreleased
22

3+
- [NEW] Add custom JSON encoder/decoder option to `Document` constructor.
34
- [NEW] Add new view parameters, `stable` and `update`, as keyword arguments to `get_view_result`.
45
- [FIXED] Case where an exception was raised after successful retry when using `doc.update_field`.
56

src/cloudant/document.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,10 @@ class Document(dict):
5353
:param database: A database instance used by the Document. Can be
5454
either a ``CouchDatabase`` or ``CloudantDatabase`` instance.
5555
:param str document_id: Optional document id used to identify the document.
56+
:param str encoder: Optional JSON encoder object.
57+
:param str decoder: Optional JSON decoder object.
5658
"""
57-
def __init__(self, database, document_id=None):
59+
def __init__(self, database, document_id=None, **kwargs):
5860
super(Document, self).__init__()
5961
self._client = database.client
6062
self._database = database
@@ -63,7 +65,8 @@ def __init__(self, database, document_id=None):
6365
self._document_id = document_id
6466
if self._document_id is not None:
6567
self['_id'] = self._document_id
66-
self.encoder = self._client.encoder
68+
self.encoder = kwargs.get('encoder') or self._client.encoder
69+
self.decoder = kwargs.get('decoder') or json.JSONDecoder
6770

6871
@property
6972
def r_session(self):
@@ -165,7 +168,7 @@ def fetch(self):
165168
resp = self.r_session.get(self.document_url)
166169
resp.raise_for_status()
167170
self.clear()
168-
self.update(resp.json())
171+
self.update(resp.json(cls=self.decoder))
169172

170173
def save(self):
171174
"""

tests/unit/document_tests.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import uuid
3131
import inspect
3232

33+
from datetime import datetime
34+
3335
from cloudant.document import Document
3436
from cloudant.error import CloudantDocumentException
3537

@@ -859,5 +861,47 @@ def test_document_request_fails_after_client_disconnects(self):
859861
finally:
860862
self.client.connect()
861863

864+
def test_document_custom_json_encoder_and_decoder(self):
865+
dt_format = '%Y-%m-%dT%H:%M:%S'
866+
867+
class DTEncoder(json.JSONEncoder):
868+
869+
def default(self, obj):
870+
if isinstance(obj, datetime):
871+
return {
872+
'_type': 'datetime',
873+
'value': obj.strftime(dt_format)
874+
}
875+
return super(DTEncoder, self).default(obj)
876+
877+
class DTDecoder(json.JSONDecoder):
878+
879+
def __init__(self, *args, **kwargs):
880+
json.JSONDecoder.__init__(self, object_hook=self.object_hook,
881+
*args, **kwargs)
882+
883+
def object_hook(self, obj):
884+
if '_type' not in obj:
885+
return obj
886+
if obj['_type'] == 'datetime':
887+
return datetime.strptime(obj['value'], dt_format)
888+
return obj
889+
890+
doc = Document(self.db, encoder=DTEncoder)
891+
doc['name'] = 'julia'
892+
doc['dt'] = datetime(2018, 7, 9, 15, 11, 10, 0)
893+
doc.save()
894+
895+
raw_doc = self.db.all_docs(include_docs=True)['rows'][0]['doc']
896+
897+
self.assertEquals(raw_doc['name'], 'julia')
898+
self.assertEquals(raw_doc['dt']['_type'], 'datetime')
899+
self.assertEquals(raw_doc['dt']['value'], '2018-07-09T15:11:10')
900+
901+
doc2 = Document(self.db, doc['_id'], decoder=DTDecoder)
902+
doc2.fetch()
903+
904+
self.assertEquals(doc2['dt'], doc['dt'])
905+
862906
if __name__ == '__main__':
863907
unittest.main()

0 commit comments

Comments
 (0)