def default_object_deserializer(obj: dict, cls: type, key_transformer: Callable[[str], str] = None, **kwargs) -> object: """ Deserialize ``obj`` into an instance of type ``cls``. If ``obj`` contains keys with a certain case style (e.g. camelCase) that do not match the style of ``cls`` (e.g. snake_case), a key_transformer should be used (e.g. KEY_TRANSFORMER_SNAKECASE). :param obj: a serialized instance of ``cls``. :param cls: the type to which ``obj`` should be deserialized. :param key_transformer: a function that transforms the keys in order to match the attribute names of ``cls``. :param kwargs: any keyword arguments that may be passed to the deserializers. :return: an instance of type ``cls``. """ if key_transformer: obj = {key_transformer(key): obj[key] for key in obj} signature_parameters = inspect.signature(cls.__init__).parameters # Loop through the signature of cls: the type we try to deserialize to. For # every required parameter, we try to get the corresponding value from # json_obj. constructor_args = dict() for signature_key, signature in signature_parameters.items(): if obj and signature_key != 'self': if signature_key in obj: cls_ = None if signature.annotation != inspect._empty: cls_ = signature.annotation value = load_impl(obj[signature_key], cls_, key_transformer=key_transformer, **kwargs) constructor_args[signature_key] = value # The constructor arguments are gathered, create an instance. instance = cls(**constructor_args) # Set any remaining attributes on the newly created instance. remaining_attrs = { attr_name: obj[attr_name] for attr_name in obj if attr_name not in constructor_args } for attr_name in remaining_attrs: loaded_attr = load_impl(remaining_attrs[attr_name], type(remaining_attrs[attr_name]), key_transformer=key_transformer, **kwargs) try: setattr(instance, attr_name, loaded_attr) except AttributeError: pass # This is raised when a @property does not have a setter. return instance
def default_list_deserializer(obj: List, cls, **kwargs) -> object: """ Deserialize a list by deserializing all items of that list. :param obj: the list that needs deserializing. :param cls: the type with a generic (e.g. List[str]). :param kwargs: any keyword arguments. :return: a deserialized list instance. """ cls_ = None if cls and hasattr(cls, '__args__'): cls_ = cls.__args__[0] return [load_impl(x, cls_, **kwargs) for x in obj]
def default_string_deserializer(obj: str, _: type = None, **kwargs) -> object: """ Deserialize a string. If the given ``obj`` can be parsed to a date, a ``datetime`` instance is returned. :param obj: the string that is to be deserialized. :param _: not used. :param kwargs: any keyword arguments. :return: the deserialized obj. """ try: # Use load_impl instead of default_datetime_deserializer to allow the # datetime deserializer to be overridden. return load_impl(obj, datetime, **kwargs) except: return obj
def default_tuple_deserializer(obj: List, cls, **kwargs) -> object: """ Deserialize a (JSON) list into a tuple by deserializing all items of that list. :param obj: the list that needs deserializing. :param cls: the type with a generic (e.g. Tuple[str, int]). :param kwargs: any keyword arguments. :return: a deserialized tuple instance. """ if hasattr(cls, '__tuple_params__'): tuple_types = cls.__tuple_params__ else: tuple_types = cls.__args__ list_ = [ load_impl(obj[i], tuple_types[i], **kwargs) for i in range(len(obj)) ] return tuple(list_)
def default_dict_deserializer(obj: dict, _: type, key_transformer: Callable[[str], str] = None, **kwargs) -> object: """ Deserialize a dict by deserializing all instances of that dict. :param obj: the dict that needs deserializing. :param key_transformer: a function that transforms the keys to a different style (e.g. PascalCase). :param cls: not used. :param kwargs: any keyword arguments. :return: a deserialized dict instance. """ key_transformer = key_transformer or (lambda key: key) new_kwargs = {**{'key_transformer': key_transformer}, **kwargs} return { key_transformer(key): load_impl(obj[key], **new_kwargs) for key in obj }