:param str|None path: Path to file to deserialize
    :param dict|list|None default: Default if file is not present, or if it's not json
    :param bool|None fatal: Abort execution on failure if True
    :param callable|None logger: Logger to use
    :return dict|list: Deserialized data from file

def read_json(path, default=None, fatal=True, logger=None):
    """
    :param str|None path: Path to file to deserialize
    :param dict|list|None default: Default if file is not present, or if it's not json
    :param bool|None fatal: Abort execution on failure if True
    :param callable|None logger: Logger to use
    :return dict|list: Deserialized data from file
    """
    path = resolved_path(path)
    if not path or not os.path.exists(path):
        if default is None:
            return abort("No file %s", short(path), fatal=(fatal, default))
        return default

    try:
        with io.open(path, "rt") as fh:
            data = json.load(fh)
            if default is not None and type(data) != type(default):
                return abort("Wrong type %s for %s, expecting %s", type(data), short(path), type(default), fatal=(fatal, default))

            if logger:
                logger("Read %s", short(path))

            return data

    except Exception as e:
        return abort("Couldn't read %s: %s", short(path), e, fatal=(fatal, default))