def deserialization() -> Conversion: annotations: dict[str, Any] = {} deserialization_namespace: dict[str, Any] = { "__annotations__": annotations } for sub in rec_subclasses(cls): annotations[sub.__name__] = Tagged[sub] # type: ignore # Add tagged fields for all its alternative constructors for constructor in _alternative_constructors.get(sub, ()): # Build the alias of the field alias = to_pascal_case(constructor.__name__) # object_deserialization uses get_type_hints, but the constructor # return type is stringified and the class not defined yet, # so it must be assigned manually constructor.__annotations__["return"] = sub # Add constructor tagged field with its conversion annotations[alias] = Tagged[sub] # type: ignore deserialization_namespace[alias] = Tagged( conversion( # Use object_deserialization to wrap constructor as deserializer deserialization=object_deserialization( constructor, type_name(alias)))) # Create the deserialization tagged union class deserialization_union = new_class( cls.__name__, (TaggedUnion, ), exec_body=lambda ns: ns.update(deserialization_namespace), ) return Conversion(lambda obj: get_tagged(obj)[1], source=deserialization_union, target=cls)
class Data2: data_field2: Field2 = field( metadata=flattened | conversion(Field2.from_field, Field2.to_field))