Custom SerializationΒΆ

If you want custom serialization logic for one of your class, or if you need to serialize external types with no type hints, you can implement custom serializer for them. Serializer is bound to the type you register and will be used when this type is encountered.

For example, you have class without type hints, or __init__ differ from actual fields. Just implement StaticSerializer

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class External:
    def __init__(self, a):
        self.b = a


class ExternalSerializer(StaticSerializer):
    real_type = External

    @classmethod
    def serialize(cls, instance: External) -> dict:
        return {'a': instance.b}

    @classmethod
    def deserialize(cls, obj: dict) -> object:
        return External(obj['a'])

Now you can serialize External

1
2
payload = serialize(External('value'))  # {'a': 'value'}
new_instance = deserialize(payload, External)  # External('value')

Like with simple types, it can be used in nested structures

1
2
3
4
5
6
7
class Container:
    def __init__(self, externals: List[External]):
        self.externals = externals


container_payload = serialize(Container([External('value')]))
new_container = deserialize(container_payload, Container)

If you need to parametrize yor serializer, you can implement generic Serializer, adding your parameters to serializers __init__. For example, you want to serialize lists of certain size with same values.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class SizedListSerializer(Serializer):
    real_type = list

    def __init__(self, size: int):
        self.size = size

    def serialize(self, instance: list) -> dict:
        if len(set(instance)) != 1:
            raise ValueError('Cannot serialize list with different values')
        return {'value': instance[0]}

    def deserialize(self, obj: dict) -> object:
        value = obj['value']
        return [value for _ in range(self.size)]


serializer = SizedListSerializer(3)

list_payload = serialize([1, 1, 1], serializer)  # {'value': 1}
new_list = deserialize(list_payload, serializer)  # [1, 1 ,1]

You can also use serializers in type hints

1
2
3
4
5
6
7
class OtherContainer:
    def __init__(self, sized_list: SizedListSerializer(3)):
        self.sized_list = sized_list


other_payload = serialize(OtherContainer([2, 2, 2]))  # {'sized_list': {'value': 2}}
new_other_container = deserialize(other_payload, OtherContainer)  # OtherContainer([2, 2, 2])

You can find this code in examples/custom_serialization.py