Skip to content
This repository has been archived by the owner on Mar 11, 2022. It is now read-only.

Commit

Permalink
Add custom JSON encoder/decoder option to Document constructor
Browse files Browse the repository at this point in the history
  • Loading branch information
smithsz committed Jul 11, 2018
1 parent fa1cf06 commit 8201741
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
@@ -1,5 +1,6 @@
# Unreleased

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

Expand Down
9 changes: 6 additions & 3 deletions src/cloudant/document.py
Expand Up @@ -53,8 +53,10 @@ class Document(dict):
:param database: A database instance used by the Document. Can be
either a ``CouchDatabase`` or ``CloudantDatabase`` instance.
:param str document_id: Optional document id used to identify the document.
:param str encoder: Optional JSON encoder object.
:param str decoder: Optional JSON decoder object.
"""
def __init__(self, database, document_id=None):
def __init__(self, database, document_id=None, **kwargs):
super(Document, self).__init__()
self._client = database.client
self._database = database
Expand All @@ -63,7 +65,8 @@ def __init__(self, database, document_id=None):
self._document_id = document_id
if self._document_id is not None:
self['_id'] = self._document_id
self.encoder = self._client.encoder
self.encoder = kwargs.get('encoder') or self._client.encoder
self.decoder = kwargs.get('decoder') or json.JSONDecoder

@property
def r_session(self):
Expand Down Expand Up @@ -165,7 +168,7 @@ def fetch(self):
resp = self.r_session.get(self.document_url)
resp.raise_for_status()
self.clear()
self.update(resp.json())
self.update(resp.json(cls=self.decoder))

def save(self):
"""
Expand Down
44 changes: 44 additions & 0 deletions tests/unit/document_tests.py
Expand Up @@ -30,6 +30,8 @@
import uuid
import inspect

from datetime import datetime

from cloudant.document import Document
from cloudant.error import CloudantDocumentException

Expand Down Expand Up @@ -859,5 +861,47 @@ def test_document_request_fails_after_client_disconnects(self):
finally:
self.client.connect()

def test_document_custom_json_encoder_and_decoder(self):
dt_format = '%Y-%m-%dT%H:%M:%S'

class DTEncoder(json.JSONEncoder):

def default(self, obj):
if isinstance(obj, datetime):
return {
'_type': 'datetime',
'value': obj.strftime(dt_format)
}
return super(DTEncoder, self).default(obj)

class DTDecoder(json.JSONDecoder):

def __init__(self, *args, **kwargs):
json.JSONDecoder.__init__(self, object_hook=self.object_hook,
*args, **kwargs)

def object_hook(self, obj):
if '_type' not in obj:
return obj
if obj['_type'] == 'datetime':
return datetime.strptime(obj['value'], dt_format)
return obj

doc = Document(self.db, encoder=DTEncoder)
doc['name'] = 'julia'
doc['dt'] = datetime(2018, 7, 9, 15, 11, 10, 0)
doc.save()

raw_doc = self.db.all_docs(include_docs=True)['rows'][0]['doc']

self.assertEquals(raw_doc['name'], 'julia')
self.assertEquals(raw_doc['dt']['_type'], 'datetime')
self.assertEquals(raw_doc['dt']['value'], '2018-07-09T15:11:10')

doc2 = Document(self.db, doc['_id'], decoder=DTDecoder)
doc2.fetch()

self.assertEquals(doc2['dt'], doc['dt'])

if __name__ == '__main__':
unittest.main()

0 comments on commit 8201741

Please sign in to comment.