JsonFormat._deflate()   C
last analyzed

Complexity

Conditions 8

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
c 1
b 0
f 0
dl 0
loc 16
rs 6.6666

1 Method

Rating   Name   Duplication   Size   Complexity  
B JsonFormat.deflateOne() 0 10 6
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
        def deflateOne(prov):
76
            if 'args' in prov:
77
                prov['args'] = [self._strcust(a) for a in prov['args']]
78
            if 'kwargs' in prov:
79
                kwargs = prov['kwargs']
80
                prov['kwargs'] = {k: self._strcust(kwargs[k]) 
81
                    for k in kwargs.keys()}
82
            if '_id' in prov:
83
                del prov['_id']
84
            return prov
85
        deflateOne(flatRecord)
86
        for version in flatRecord.get('_versions', []):
87
            deflateOne(version)
88
        return flatRecord
89
90
    def _strcust(self, val):
91
        """Stringify an object that is not of a simple type."""
92
        if not isinstance(val, (str, unicode, int, float, bool, type(None))):
93
            return str(val)
94
        return val
95
96
# Taken from http://taketwoprogramming.blogspot.com/2009/06/subclassing-jsonencoder-and-jsondecoder.html
97
98
class DateTimeAwareJSONEncoder(json.JSONEncoder):
99
    """ 
100
    Converts a python object, where datetime and timedelta objects are converted
101
    into objects that can be decoded using the DateTimeAwareJSONDecoder.
102
    """
103
    def default(self, obj):
104
        if isinstance(obj, datetime):
105
            return {
106
                '__type__' : 'datetime',
107
                'year' : obj.year,
108
                'month' : obj.month,
109
                'day' : obj.day,
110
                'hour' : obj.hour,
111
                'minute' : obj.minute,
112
                'second' : obj.second,
113
                'microsecond' : obj.microsecond,
114
            }
115
116
        else:
117
            return json.JSONEncoder.default(self, obj)
118
119
class DateTimeAwareJSONDecoder(json.JSONDecoder):
120
    """ 
121
    Converts a json string, where datetime and timedelta objects were converted
122
    into objects using the DateTimeAwareJSONEncoder, back into a python object.
123
    """
124
125
    def __init__(self, **kwargs):
126
        json.JSONDecoder.__init__(self, object_hook=self.dict_to_object)
127
128
    def dict_to_object(self, d):
129
        if '__type__' not in d:
130
            return d
131
132
        type = d.pop('__type__')
133
        if type == 'datetime':
134
            return datetime(**d)
135
        else:
136
            # Oops... better put this back together.
137
            d['__type__'] = type
138
            return d
139