Refactored sdp serializers

This commit is contained in:
2019-10-21 16:13:56 +02:00
parent 3f423454c8
commit 8f1c2ed818
13 changed files with 1353 additions and 13 deletions
+167
View File
@@ -0,0 +1,167 @@
import json
import pickle
import datetime
import struct
import io
def json_default_converter(o):
"""
Default formatter for json
It's used when the json serializer does not know
how to serialise a type
:param o:
:return:
"""
if isinstance(o, (datetime.date, datetime.datetime)):
return o.isoformat()
class Serializer:
HEADER_FORMAT = "cH"
def __init__(self):
self._cache = []
# add builtin serializers
self._cache.append(EventSerializer())
self._cache.append(PickleSerializer())
def register(self, serializer):
"""
Register the list of all know serializers
:param serializer:
:return:
"""
self._cache.append(serializer)
def serialize(self, obj):
"""
Get the stream representation of an object
:param obj:
:return:
"""
serializers = [s for s in self._cache if s.match(obj)]
if not serializers:
raise TypeError(f"Don't know how to serialize {type(obj)}")
serializer = serializers[0]
stream = io.BytesIO()
header = struct.pack(Serializer.HEADER_FORMAT, bytes(serializer.name, "utf-8"), serializer.version)
stream.write(header)
return serializer.dump(stream, obj)
def deserialize(self, stream):
"""
Loads an object from its stream representation
:param stream:
:return:
"""
header = struct.unpack(Serializer.HEADER_FORMAT, stream.read(4))
serializers = [s for s in self._cache if s.name == header[0].decode("utf-8") and s.version == header[1]]
if not serializers:
raise TypeError(f"Don't know how serializer name={header[0]}, version={header[1]}")
serializer = serializers[0]
return serializer.load(stream)
class BaseSerializer:
def __init__(self, name, version):
"""
Create a serializer, given a name and a version
:param name:
:param version:
:return:
"""
self.name = name
self.version = version
@staticmethod
def match(obj):
"""
Returns true if self can serialize obj
:param obj:
:return:
"""
pass
def dump(self, stream, obj):
"""
Returns the byte representation of how the object should be serialized
:param stream: to write to
:param obj:
:return: stream of bytes
"""
pass
def load(self, stream):
"""
From a stream of bytes, create the object
:param stream:
:return: object
"""
pass
@staticmethod
def get_class(kls):
parts = kls.split('.')
module = ".".join(parts[:-1])
m = __import__(module)
for comp in parts[1:]:
m = getattr(m, comp)
return m
@staticmethod
def get_full_qualified_name(obj):
module = obj.__class__.__module__
if module is None or module == str.__class__.__module__:
return obj.__class__.__name__ # Avoid reporting __builtin__
else:
return module + '.' + obj.__class__.__name__
class EventSerializer(BaseSerializer):
@staticmethod
def match(obj):
return BaseSerializer.get_full_qualified_name(obj) == "sdp.sheerkaDataProvider.Event"
def __init__(self):
BaseSerializer.__init__(self, "E", 1)
def dump(self, stream, obj):
stream.write(obj.to_json().encode("utf-8"))
stream.seek(0)
return stream
def load(self, stream):
json_stream = stream.read().decode("utf-8")
json_message = json.loads(json_stream)
event = BaseSerializer.get_class("sdp.sheerkaDataProvider.Event")()
event.from_json(json_message)
return event
class PickleSerializer(BaseSerializer):
@staticmethod
def match(obj):
return BaseSerializer.get_full_qualified_name(obj) == "sdp.sheerkaDataProvider.State"
def __init__(self):
BaseSerializer.__init__(self, "P", 1)
def dump(self, stream, obj):
stream.write(pickle.dumps(obj))
stream.seek(0)
return stream
def load(self, stream):
return pickle.loads(stream.read())