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

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

    from xsdata.formats.dataclass.parsers.bases import NodeParser, Parsed
    from xsdata.formats.dataclass.parsers.handlers import default_handler
    from xsdata.formats.dataclass.parsers.mixins import XmlHandler, 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)