Class serialization using Python json package works fine with class data members, which are built-in Python data types (ex. integer, string, boolean, float, list, dictionary). However, custom data types such as class instance as data member are non-serializable. JsonHandler cannot handle such non-serializable data types either. As a safety net for facing such case, ObjectToFile method will automatically remove all non-serializable items from dictionary before serialization. For handling this type of serialization issues, I assume there are several more sophisticated third-party packages available.
Finally, it should be noted that when performing deserialization, JsonHandler transforms JSON string into dictionary, then transforms dictionary into object. Correspondingly, when performing serialization, it transforms object into dictionary, then transforms dictionary into JSON string. These operations (including nested methods) are adding some extra complexity into this otherwise simple and straightforward utility class.
Assume we would like to hydrate (serialize) Configurations class instance into JSON file. Note, that this class is actually containing non-serializable data type as its data member (class Whatever). This specific data member will be completely ignored in a process of serialization. However, as JSON file will be deserialized, instance of data member G will be created on class Configurations constructor, based on data members A and B (which are both serializable data types).
import json # class for handling transformations between custom object and JSON file class JsonHandler: # transform json file to custom object def FileToObject(file): # nested function: transform dictionary to custom object def DictionaryToObject(dic): if("__class__" in dic): class_name = dic.pop("__class__") module_name = dic.pop("__module__") module = __import__(module_name) class_ = getattr(module, class_name) obj = class_(**dic) else: obj = dic return obj return DictionaryToObject(json.load(open(file, 'r'))) # transform custom object to json file def ObjectToFile(obj, file): # nested function: check whether an object can be json serialized def IsSerializable(obj): check = True try: # throws, if an object is not serializable json.dumps(obj) except: check = False return check # nested function: transform custom object to dictionary def ObjectToDictionary(obj): dic = { "__class__": obj.__class__.__name__, "__module__": obj.__module__ } dic.update(obj.__dict__) # remove all non-serializable items from dictionary before serialization keysToBeRemoved = [] for k, v in dic.items(): if(IsSerializable(v) == False): keysToBeRemoved.append(k) [dic.pop(k, None) for k in keysToBeRemoved] return dic json.dump(ObjectToDictionary(obj), open(file, 'w')) class Configurations(object): def __init__(self, A, B, C, D, E, F): # serializable data types self.A = A self.B = B self.C = C self.D = D self.E = E self.F = F # non-serializable data type self.G = Whatever(A, B) class Whatever(object): def __init__(self, A, B): self.A = A self.B = B # create class instance using 'primitive types' new_config = Configurations(100, 3.14, True, [1, 2, 3], 'qwerty', { 'd1':1, 'd2':2 }) # print class members print('printing data members of a newly created class instance:') print(new_config.A) print(new_config.B) print(new_config.C) print(new_config.D) print(new_config.E) print(new_config.F) print(new_config.G.A) print(new_config.G.B) print() # write object to json file JsonHandler.ObjectToFile(new_config, '/home/mikejuniperhill/config.json') # read object from json file restored_config = JsonHandler.FileToObject('/home/mikejuniperhill/config.json') print('printing data members of a restored class instance:') print(restored_config.A) print(restored_config.B) print(restored_config.C) print(restored_config.D) print(restored_config.E) print(restored_config.F) print(restored_config.G.A) print(restored_config.G.B)
Program execution in terminal is shown below.
Thanks for reading.
-Mike
No comments:
Post a Comment