def serialize(obj, no_dump=False):
    """
    Serialize an object.

    Returns a dict containing an `_error` property if a MemoryError happens during the
    object serialization. See #369.

    :param obj: the object to serialize
    :type obj: alignak.objects.item.Item | dict | list | str
    :param no_dump: if True return dict, otherwise return a json
    :type no_dump: bool
    :return: dict or json dumps dict with the following structure ::

       {'__sys_python_module__': "%s.%s" % (o_cls.__module__, o_cls.__name__)
       'content' : obj.serialize()}
    :rtype: dict | str
    """
    # print("Serialize (%s): %s" % (no_dump, obj))

    if hasattr(obj, "serialize") and isinstance(obj.serialize, collections.Callable):
        o_dict = {
            '__sys_python_module__': "%s.%s" % (obj.__class__.__module__, obj.__class__.__name__),
            'content': obj.serialize()
        }

    elif isinstance(obj, dict):
        o_dict = {}
        for key, value in list(obj.items()):
            o_dict[key] = serialize(value, True)

    elif isinstance(obj, (list, set)):
        o_dict = [serialize(item, True) for item in obj]

    else:
        o_dict = obj

    if no_dump:
        return o_dict

    result = None
    try:
        result = json.dumps(o_dict, ensure_ascii=False)
    except MemoryError:
        return {'_error': 'Not enough memory on this computer to correctly manage Alignak '
                          'objects serialization! '
                          'Sorry for this, please log an issue in the project repository.'}

    return result