def _serialize_custom_object(obj):
    """Serialize a user-defined object to JSON.

    This function gets called when `json.dumps` cannot serialize
    an object and returns a serializable dictionary containing enough
    metadata to recontrust the original object.

    Parameters
    ----------
    obj: Object
        The object to serialize

    Returns
    -------
    dict_obj: A serializable dictionary with enough metadata to reconstruct
              `obj`

    Exceptions
    ----------
    TypeError:
        Raise if `obj` does not contain a `to_json` attribute
    """
    if not hasattr(obj, 'to_json'):
        raise TypeError(
            f'class {type(obj)} does not expose a `to_json` function')
    obj_type = type(obj)
    dict_obj = {'__class__': obj.__class__.__name__, '__module__': obj.
        __module__, '__data__': obj_type.to_json(obj)}
    return dict_obj
