Completed
Push — master ( b66745...9821e6 )
by Jasper
02:18 queued 58s
created

DateTimeAwareJSONDecoder.dict_to_object()   A

Complexity

Conditions 3

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
c 1
b 0
f 0
dl 0
loc 11
rs 9.4285
1
import json, copy
2
from datetime import datetime
3
from niprov.format import Format
4
5
6
class JsonFormat(Format):
7
    """Helper to convert provenance data to and from json encoded strings.
8
    """
9
10
    def __init__(self, dependencies):
11
        super(JsonFormat, self).__init__(dependencies)
12
        self.fileExtension = 'json'
13
        self.file = dependencies.getFileFactory()
14
15
    def serializeSingle(self, record):
16
        """
17
        Convert one provenance item from its native python dict type to
18
        a json string.
19
20
        Args:
21
            record (dict): The provenance item to convert.
22
23
        Returns:
24
            str: Json version of the provenance.
25
        """
26
        flat = self._deflate(record.provenance)
27
        return json.dumps(flat, cls=DateTimeAwareJSONEncoder)
28
29
    def deserialize(self, jsonRecord):
30
        """
31
        Convert one provenance item from its json string version to the 
32
        native python dictionary format.
33
34
        Args:
35
            jsonRecord (str): The provenance item to convert as json-encoded 
36
                string.
37
38
        Returns:
39
            dict: Python dictionary of the provenance.
40
        """
41
        provenance = json.loads(jsonRecord, cls=DateTimeAwareJSONDecoder)
42
        return self.file.fromProvenance(provenance)
43
44
    def serializeList(self, listOfRecords):
45
        """
46
        Convert a list of provenance items from its native list of python dict 
47
        type to a json string.
48
49
        Args:
50
            listOfRecords (list): The provenance items to convert.
51
52
        Returns:
53
            str: Json version of the provenance items.
54
        """
55
        flatRecords = [self._deflate(r.provenance) for r in listOfRecords]
56
        return json.dumps(flatRecords, cls=DateTimeAwareJSONEncoder)
57
58
    def deserializeList(self, jsonListOfRecords):
59
        """
60
        Convert a list of provenance items from its json string version to the 
61
        a list of the native python dictionary format.
62
63
        Args:
64
            jsonListOfRecords (str): The provenance items to convert as 
65
                json-encoded string.
66
67
        Returns:
68
            list: Python list of dictionaries of the provenance.
69
        """
70
        provenanceList = json.loads(jsonListOfRecords, cls=DateTimeAwareJSONDecoder)
71
        return [self.file.fromProvenance(p) for p in provenanceList]
72
73
    def _deflate(self, record):
74
        flatRecord = copy.deepcopy(record)
75
        if 'args' in record:
76
            flatRecord['args'] = [self._strcust(a) for a in record['args']]
77
        if 'kwargs' in record:
78
            kwargs = record['kwargs']
79
            flatRecord['kwargs'] = {k: self._strcust(kwargs[k]) 
80
                for k in kwargs.keys()}
81
        if '_id' in record:
82
            del flatRecord['_id']
83
        return flatRecord
84
85
    def _strcust(self, val):
86
        """Stringify an object that is not of a simple type."""
87
        if not isinstance(val, (str, unicode, int, float, bool, type(None))):
88
            return str(val)
89
        return val
90
91
# Taken from http://taketwoprogramming.blogspot.com/2009/06/subclassing-jsonencoder-and-jsondecoder.html
92
93
class DateTimeAwareJSONEncoder(json.JSONEncoder):
94
    """ 
95
    Converts a python object, where datetime and timedelta objects are converted
96
    into objects that can be decoded using the DateTimeAwareJSONDecoder.
97
    """
98
    def default(self, obj):
99
        if isinstance(obj, datetime):
100
            return {
101
                '__type__' : 'datetime',
102
                'year' : obj.year,
103
                'month' : obj.month,
104
                'day' : obj.day,
105
                'hour' : obj.hour,
106
                'minute' : obj.minute,
107
                'second' : obj.second,
108
                'microsecond' : obj.microsecond,
109
            }
110
111
        else:
112
            return json.JSONEncoder.default(self, obj)
113
114
class DateTimeAwareJSONDecoder(json.JSONDecoder):
115
    """ 
116
    Converts a json string, where datetime and timedelta objects were converted
117
    into objects using the DateTimeAwareJSONEncoder, back into a python object.
118
    """
119
120
    def __init__(self, **kwargs):
121
        json.JSONDecoder.__init__(self, object_hook=self.dict_to_object)
122
123
    def dict_to_object(self, d):
124
        if '__type__' not in d:
125
            return d
126
127
        type = d.pop('__type__')
128
        if type == 'datetime':
129
            return datetime(**d)
130
        else:
131
            # Oops... better put this back together.
132
            d['__type__'] = type
133
            return d
134