# Buggy code
def _ConvertDictToObject(cls, json_dict):
    """Converts a JSON dict into an object.

    The dictionary of the JSON serialized objects consists of:
    {
        '__type__': 'AttributeContainer'
        '__container_type__': ...
        ...
    }

    Here '__type__' indicates the object base type. In this case
    'AttributeContainer'.

    '__container_type__' indicates the attribute container type.

    The rest of the elements of the dictionary make up the attributes.

    Args:
      json_dict (dict[str, object]): JSON serialized objects.

    Returns:
      AttributeContainer|dict|list|tuple: deserialized object.

    Raises:
      ValueError: if the class type, container type or attribute type
          of event data container is not supported.
    """
    # Use __type__ to indicate the object class type.
    class_type = json_dict.get("__type__", None)
    if not class_type:
        # Dealing with a regular dict.
        return json_dict

    if class_type == "bytes":
        return binascii.a2b_qp(json_dict["stream"])

    if class_type == "tuple":
        return tuple(cls._ConvertListToObject(json_dict["values"]))

    if class_type == "collections.Counter":
        return cls._ConvertDictToCollectionsCounter(json_dict)

    if class_type == "AttributeContainer":
        # Use __container_type__ to indicate the attribute container type.
        container_type = json_dict.get("__container_type__", None)

    # Since we would like the JSON as flat as possible we handle decoding
    # a path specification.
    elif class_type == "PathSpec":
        return cls._ConvertDictToPathSpec(json_dict)

    else:
        raise ValueError("Unsupported class type: {0:s}".format(class_type))

    container_class = (
        containers_manager.AttributeContainersManager.GetAttributeContainer(
            container_type
        )
    )
    if not container_class:
        raise ValueError("Unsupported container type: {0:s}".format(container_type))

    container_object = container_class()
    supported_attribute_names = container_object.GetAttributeNames()
    for attribute_name, attribute_value in iter(json_dict.items()):
        # Be strict about which attributes to set in non event data attribute
        # containers.
        if (
            container_type != "event_data"
            and attribute_name not in supported_attribute_names
        ):
            if attribute_name not in ("__container_type__", "__type__"):
                logger.debug(
                    (
                        "[ConvertDictToObject] unsupported attribute name: {0:s}.{1:s}"
                    ).format(container_type, attribute_name)
                )

            continue

        if isinstance(attribute_value, dict):
            attribute_value = cls._ConvertDictToObject(attribute_value)

        elif isinstance(attribute_value, list):
            attribute_value = cls._ConvertListToObject(attribute_value)

        if container_type == "event_data":
            if isinstance(attribute_value, py2to3.BYTES_TYPE):
                raise ValueError(
                    (
                        "Event data attribute value: {0:s} of type bytes is not "
                        "supported."
                    ).format(attribute_name)
                )

            if isinstance(attribute_value, dict):
                raise ValueError(
                    (
                        "Event data attribute value: {0:s} of type dict is not "
                        "supported."
                    ).format(attribute_name)
                )

        setattr(container_object, attribute_name, attribute_value)

    return container_object


# Traceback (ValueError)
Traceback (most recent call last):
File "plaso/tools/log2timeline.py", line 97, in <module>
if not Main():
File "plaso/tools/log2timeline.py", line 72, in Main
tool.ExtractEventsFromSources()
File "plaso/plaso/cli/log2timeline_tool.py", line 448, in ExtractEventsFromSources
worker_memory_limit=self._worker_memory_limit)
File "plaso/plaso/multi_processing/task_engine.py", line 812, in ProcessSources
self._ProcessSources(source_path_specs, storage_writer)
File "plaso/plaso/multi_processing/task_engine.py", line 343, in _ProcessSources
self._ScheduleTasks(storage_writer)
File "plaso/plaso/multi_processing/task_engine.py", line 449, in _ScheduleTasks
self._MergeTaskStorage(storage_writer)
File "plaso/plaso/multi_processing/task_engine.py", line 256, in _MergeTaskStorage
maximum_number_of_containers=self._MAXIMUM_NUMBER_OF_CONTAINERS)
File "plaso/plaso/storage/sqlite/merge_reader.py", line 278, in MergeAttributeContainers
self._active_container_type, serialized_data)
File "plaso/plaso/storage/interface.py", line 600, in _DeserializeAttributeContainer
attribute_container = self._serializer.ReadSerialized(serialized_string)
File "plaso/plaso/serializer/json_serializer.py", line 404, in ReadSerialized
return cls.ReadSerializedDict(json_dict)
File "plaso/plaso/serializer/json_serializer.py", line 423, in ReadSerializedDict
json_object = cls._ConvertDictToObject(json_dict)
File "plaso/plaso/serializer/json_serializer.py", line 252, in _ConvertDictToObject
'supported.').format(attribute_name))
ValueError: Event data attribute value: return_value of type dict is not supported.

# Fixed code
def _ConvertDictToObject(cls, json_dict):
    """Converts a JSON dict into an object.

    The dictionary of the JSON serialized objects consists of:
    {
        '__type__': 'AttributeContainer'
        '__container_type__': ...
        ...
    }

    Here '__type__' indicates the object base type. In this case
    'AttributeContainer'.

    '__container_type__' indicates the attribute container type.

    The rest of the elements of the dictionary make up the attributes.

    Args:
      json_dict (dict[str, object]): JSON serialized objects.

    Returns:
      AttributeContainer|dict|list|tuple: deserialized object.

    Raises:
      ValueError: if the class type, container type or attribute type
          of event data container is not supported.
    """
    # Use __type__ to indicate the object class type.
    class_type = json_dict.get("__type__", None)
    if not class_type:
        # Dealing with a regular dict.
        return json_dict

    if class_type == "bytes":
        return binascii.a2b_qp(json_dict["stream"])

    if class_type == "tuple":
        return tuple(cls._ConvertListToObject(json_dict["values"]))

    if class_type == "collections.Counter":
        return cls._ConvertDictToCollectionsCounter(json_dict)

    if class_type == "AttributeContainer":
        # Use __container_type__ to indicate the attribute container type.
        container_type = json_dict.get("__container_type__", None)

    # Since we would like the JSON as flat as possible we handle decoding
    # a path specification.
    elif class_type == "PathSpec":
        return cls._ConvertDictToPathSpec(json_dict)

    else:
        raise ValueError("Unsupported class type: {0:s}".format(class_type))

    container_object = (
        containers_manager.AttributeContainersManager.CreateAttributeContainer(
            container_type
        )
    )

    supported_attribute_names = container_object.GetAttributeNames()
    for attribute_name, attribute_value in iter(json_dict.items()):
        # Be strict about which attributes to set in non event data attribute
        # containers.
        if (
            container_type != "event_data"
            and attribute_name not in supported_attribute_names
        ):
            if attribute_name not in ("__container_type__", "__type__"):
                logger.debug(
                    (
                        "[ConvertDictToObject] unsupported attribute name: {0:s}.{1:s}"
                    ).format(container_type, attribute_name)
                )

            continue

        if isinstance(attribute_value, dict):
            attribute_value = cls._ConvertDictToObject(attribute_value)

        elif isinstance(attribute_value, list):
            attribute_value = cls._ConvertListToObject(attribute_value)

        if container_type == "event_data":
            if isinstance(attribute_value, py2to3.BYTES_TYPE):
                raise ValueError(
                    (
                        "Event data attribute value: {0:s} of type bytes is not "
                        "supported."
                    ).format(attribute_name)
                )

            if isinstance(attribute_value, dict):
                raise ValueError(
                    (
                        "Event data attribute value: {0:s} of type dict is not "
                        "supported."
                    ).format(attribute_name)
                )

        setattr(container_object, attribute_name, attribute_value)

    return container_object