Package: yasoo
yasoo: Serialize the Data You HaveA python serializer ofattrsanddataclassobjects that doesn't rely on type hints.Why yasooyasooadds type data to the serialized data, so deserialization doesn't need to rely on type hints.Moreover, if you have a field that can contain multiple types of values, or a field which contains some specific implementation of an abstract class,yasoohas no problem with that.For example, this code works fine:from attr import attrs, attrib
from yasoo import serialize, deserialize

@attrs
class Foo:
    a = attrib()

@attrs
class Bar:
    foo: Foo = attrib()

serialized = serialize(Bar(foo=5))
assert(deserialize(serialized).foo == 5)UsageBasic UsageFor simple objects, use:from yasoo import serialize, deserialize

with open(path, 'w') as f:
    json.dump(serialize(obj), f)

with open(path) as f:
    obj = deserizlie(json.load(f))Advanced UsageDeserializing Collections of ObjectsYou can deserialize collections of objects:from attr import attrs, attrib
from yasoo import serialize, deserialize
from yasoo.typing import List_

@attrs
class Foo:
    a = attrib()

foos = [Foo(a=i) for i in range(5)]
serialized = serialize(foos)
de_foos = deserialize(serialized, obj_type=List_[Foo])
assert de_foos == foosNotice that passing the object type asList[Foo]won't give you the type
ofde_foos, but usingyasoo.typing.List_will fix this.Custom (De)SerializersFor objects that need custom serialization/deserialization, you can register your own methods:from attr import attrs, attrib, asdict
from yasoo import serialize, deserialize, serializer, deserializer

@attrs
class Foo:
    bar = attrib(converter=lambda x: x * 2)

    def set_foobar(self, foobar):
        self.foobar = foobar

    @serializer
    def serialize(self: 'Foo'):
        result = asdict(self)
        if hasattr(self, 'foobar'):
            result['foobar'] = self.foobar
        return result

    @staticmethod
    @deserializer
    def deserialize(data: dict) -> 'Foo':
        foo = Foo(data['bar'] / 2)
        if 'foobar' in data:
            foo.set_foobar(data['foobar'])
        return fooNotice that registering custom methods with forward reference (i.e.'Foo'instead ofFoo) requires passing theglobalsparameter toserialize/deserialize, e.g.serialize(obj, globals=globals())Using Type HintsIf you want to avoid having the__typekey in your serialized data, you can set thetype_keyparameter toNonewhen callingserialize.For this to work all fields in the serialized class that are not json-serializable should have a type hint.Serializing SequencesBy default all sequences found in the data will be converted tolistin the serialization process.
If you want to be able to deserialize them back to anything other than a list, set thepreserve_iterable_typesparameter toTruewhen callingserialize.Note: setting thepreserve_iterable_typesparameter toTruewill cause all iterables that are notlistto be serialized as dictionaries with their type saved under thetype_key.Multiple Serialization Methods For The Same TypeIf you want to define a custom serialization method for a type for a specific use case, without affecting the default serializer, you can create another instance ofSerializerand register the method on that instance. For example:from yasoo import Serializer, serializer, serialize

@serializer
def serialize_foo(foo: Foo):
    return {'bar': foo.bar}

my_serializer = Serializer()

@my_serializer.register()
def serialize_foo_another_way(foo: Foo):
    return {'bar': foo.bar * 2}

serialize(Foo(bar=5))  # returns {'bar': 5, '__type': 'Foo'}
my_serializer.serialize(Foo(bar=5))  # returns {'bar': 10, '__type': 'Foo'}