Skip to content

Conversation

@sdhiscocks
Copy link

Resolves #45

@github-actions
Copy link

github-actions bot commented Jul 14, 2025

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@sdhiscocks
Copy link
Author

I have read the CLA Document and I hereby sign the CLA

@bpietropaoli
Copy link

I'm sorry but I would reject this version as it does the lookup for every serialisation/deserialisation.
As I mentioned in the issue conversation, I think you can easily actually register the subclasses by exploring cls.subclasses() recursively. :)

Copy link

@bpietropaoli bpietropaoli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you follow my suggestions, I've basically reverted almost all your changes but added an actual registration of subclasses with the same decoder/encoder directly in the registration method so the hierarchy is search only once at the registration and not at every serialisation/deserialisation.

It should do the same thing, just cleaner (in my humble opinion).

Comment on lines 94 to 97
if include_subclasses:
UniversalJSONDecoder._multi_decoders[obj_type] = decoding_function
else:
UniversalJSONDecoder._decoders[obj_type] = decoding_function

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if include_subclasses:
UniversalJSONDecoder._multi_decoders[obj_type] = decoding_function
else:
UniversalJSONDecoder._decoders[obj_type] = decoding_function
UniversalJSONDecoder._decoders[obj_type] = decoding_function
# Explore the subclasses tree:
if include_subclasses:
subclasses = obj_type.__subclasses__()
all_subclasses = [] + subclasses
while subclasses:
new_subclasses = []
for cls in subclasses:
new_subclasses += cls.__subclasses__()
all_subclasses += new_subclasses
subclasses = new_subclasses
for cls in all_subclasses:
UniversalJSONDecoder._decoders[cls] = decoding_function

By doing so, every other change becomes obsolete. It does the lookup at the registration time only, and actually registers the subclasses. ;)

def clear_decoders(cls) -> None:
"""Clear all registered decoders. Primarily for testing purposes."""
cls._decoders.clear()
cls._multi_decoders.clear()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cls._multi_decoders.clear()

Comment on lines 107 to 113
"""Check if a decoder is registered for the given type."""
if obj_type in cls._decoders:
return True
for obj_subtype in obj_type.mro():
if obj_subtype in cls._multi_decoders:
return True
return False

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""Check if a decoder is registered for the given type."""
if obj_type in cls._decoders:
return True
for obj_subtype in obj_type.mro():
if obj_subtype in cls._multi_decoders:
return True
return False
"""Check if a decoder is registered for the given type. Primarily for testing purposes."""
return obj_type in cls._decoders

Comment on lines 116 to 123
def get_registered_decoder(cls, obj_type: Type[Any]) -> Decoder | None:
"""Get the registered decoder for the given type."""
if obj_type in cls._decoders:
return cls._decoders[obj_type]
for obj_subtype in obj_type.mro():
if obj_subtype in cls._multi_decoders:
return cls._multi_decoders[obj_subtype]
return None

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def get_registered_decoder(cls, obj_type: Type[Any]) -> Decoder | None:
"""Get the registered decoder for the given type."""
if obj_type in cls._decoders:
return cls._decoders[obj_type]
for obj_subtype in obj_type.mro():
if obj_subtype in cls._multi_decoders:
return cls._multi_decoders[obj_subtype]
return None
def get_registered_decoder(cls, obj_type: Type[Any]) -> Callable[[Dict[str, Any]], Any] | None:
"""Get the registered decoder for the given type. Primarily for testing purposes."""
return cls._decoders.get(obj_type)

if (func := self.get_registered_encoder(obj_class)) is not None:
try:
the_dict = UniversalJSONEncoder._encoders[type(obj)](obj)
the_dict = func(obj)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
the_dict = func(obj)
the_dict = UniversalJSONEncoder._encoders[type(obj)](obj)

Comment on lines +163 to +164
func_name = func.__name__
error_msg = f"Encoding function {func_name} used for type '{obj_class}' raised an exception: {exc}."

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func_name = func.__name__
error_msg = f"Encoding function {func_name} used for type '{obj_class}' raised an exception: {exc}."
func_name = UniversalJSONEncoder._encoders[type(obj)].__name__
error_msg = f"Encoding function {func_name} used for type '{type(obj)}' raised an exception: {exc}."

pass
except Exception as exc:
error_msg = f"Method __json_encode__() used for type '{type(obj)}' raised an exception."
error_msg = f"Method __json_encode__() used for type '{obj_class}' raised an exception."

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
error_msg = f"Method __json_encode__() used for type '{obj_class}' raised an exception."
error_msg = f"Method __json_encode__() used for type '{type(obj)}' raised an exception."

# If nothing worked, raise an exception like the default JSON encoder would:
if not already_encoded:
raise TypeError(f"Type {type(obj)} is not JSON serializable. Value: {obj}")
raise TypeError(f"Type {obj_class} is not JSON serializable. Value: {obj}")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
raise TypeError(f"Type {obj_class} is not JSON serializable. Value: {obj}")
raise TypeError(f"Type {type(obj)} is not JSON serializable. Value: {obj}")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new feature should include a unit test too, testing this functionality in particular.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants