1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
|
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)
|