# Source code for xsdata.formats.dataclass.parsers.xml

    from dataclasses import dataclass
    from dataclasses import field
    from typing import Any
    from typing import Dict
    from typing import List
    from typing import Optional
    from typing import Type

    from xsdata.formats.dataclass.parsers.bases import NodeParser
    from xsdata.formats.dataclass.parsers.bases import Parsed
    from xsdata.formats.dataclass.parsers.handlers import default_handler
    from xsdata.formats.dataclass.parsers.mixins import XmlHandler
    from xsdata.formats.dataclass.parsers.mixins import XmlNode
    from xsdata.models.enums import EventType
    from xsdata.utils.namespaces import local_name
    from xsdata.utils.text import snake_case

    @dataclass
    class XmlParser(NodeParser):
        """
        Default Xml parser for dataclasses.

        :param config: Parser configuration
        :param context: Model context provider
        :param handler: Override default XmlHandler
        :ivar ms_map: The prefix-URI map generated during parsing
        """

        handler: Type[XmlHandler] = field(default=default_handler())

    @dataclass
    class UserXmlParser(NodeParser):
        """
        User Xml parser for dataclasses with hooks for emitting events to alter the
        behavior when an elements starts or ends.

        :param config: Parser configuration
        :param context: Model context provider
        :param handler: Override default XmlHandler
        :ivar ms_map: The prefix-URI map generated during parsing
        :ivar emit_cache: Qname to event name cache
        """

        handler: Type[XmlHandler] = field(default=default_handler())
        emit_cache: Dict = field(init=False, default_factory=dict)

        def start(
            self,
            clazz: Optional[Type],
            queue: List[XmlNode],
            objects: List[Parsed],
            qname: str,
            attrs: Dict,
            ns_map: Dict,
        ):
            super().start(clazz, queue, objects, qname, attrs, ns_map)
            self.emit_event(EventType.START, qname, attrs=attrs)

        def end(
            self,
            queue: List[XmlNode],
            objects: List[Parsed],
            qname: str,
            text: Optional[str],
            tail: Optional[str],
        ) -> bool:
            result = super().end(queue, objects, qname, text, tail)
            if result:
                self.emit_event(EventType.END, qname, obj=objects[-1][1])
            return result

        def emit_event(self, event: str, name: str, **kwargs: Any):
            """
            Propagate event to subclasses.

            Match event and name to a subclass method and trigger it with
            any input keyword arguments.

            Example::

                event=start, name={urn}bookTitle -> start_booking_title(**kwargs)

            :param event: Event type start|end
            :param name: Element qualified name
            :param kwargs: Event keyword arguments
            """

            key = (event, name)
            if key not in self.emit_cache:
                method_name = f"{event}_{snake_case(local_name(name))}"
                self.emit_cache[key] = getattr(self, method_name, None)

            method = self.emit_cache[key]
            if method:
                method(**kwargs)