Source code for xsdata.formats.dataclass.models.elements
from dataclasses import dataclass
from dataclasses import field
from enum import auto
from enum import IntEnum
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from typing import Type
from xsdata.models.enums import FormType
from xsdata.models.enums import NamespaceType
from xsdata.utils import text
[docs]@dataclass(frozen=True)
class XmlVar:
"""
Dataclass field bind metadata.
:param name: Field name.
:param qname: Namespace qualified local name.
:param init: Field is present in the constructor parameters.
:param mixed: Field supports mixed content.
:param tokens: Use a list to map simple values.
:param nillable: Allow empty content elements rendering.
:param dataclass: Specify whether the field type is a dataclass.
:param sequential: Switch to sequential rendering with other sequential siblings
:param list_element: Specify whether the field represents a list of elements
:param default: Field default value or factory
:param types: Field simple types.
:param namespaces: Field list of the all the possible namespaces.
"""
name: str
qname: str
init: bool = True
mixed: bool = False
tokens: bool = False
nillable: bool = False
dataclass: bool = False
sequential: bool = False
list_element: bool = False
default: Any = None
types: List[Type] = field(default_factory=list)
namespaces: List[str] = field(default_factory=list)
@property
def clazz(self) -> Optional[Type]:
"""Return the first type if field is bound to a dataclass."""
return self.types[0] if self.dataclass else None
@property
def is_any_type(self) -> bool:
"""Return whether the field type is xs:anyType."""
return False
@property
def is_attribute(self) -> bool:
"""Return whether the field is derived from xs:attribute."""
return False
@property
def is_attributes(self) -> bool:
"""Return whether the field is derived from xs:anyAttributes."""
return False
@property
def is_element(self) -> bool:
"""Return whether the field is derived from xs:element."""
return False
@property
def is_list(self) -> bool:
"""Return whether the field is a list of elements."""
return self.list_element
@property
def is_mixed_content(self) -> bool:
"""Return whether the field is a mixed content list of of elements."""
return False
@property
def is_clazz_union(self) -> bool:
return self.dataclass and len(self.types) > 1
@property
def is_text(self) -> bool:
"""Return whether the field is a text element."""
return False
@property
def is_wildcard(self) -> bool:
"""Return whether the field is a text element."""
return False
@property
def local_name(self) -> str:
"""The element local name."""
return text.split_qname(self.qname)[1]
@property
def namespace(self) -> Optional[str]:
"""The element namespace."""
return text.split_qname(self.qname)[0]
[docs] def matches(self, qname: str) -> bool:
"""
Match the field qualified local name to the given qname.
Return True automatically if the local name is a wildcard.
"""
return qname in (self.qname, "*")
[docs]@dataclass(frozen=True)
class XmlElement(XmlVar):
"""Dataclass field bind metadata for xml elements."""
@property
def is_element(self) -> bool:
return True
@property
def is_any_type(self) -> bool:
return len(self.types) == 1 and self.types[0] is object
[docs]@dataclass(frozen=True)
class XmlWildcard(XmlVar):
"""Dataclass field bind metadata for xml wildcard elements."""
@property
def is_mixed_content(self) -> bool:
return self.mixed
@property
def is_wildcard(self) -> bool:
return True
@property
def is_any_type(self) -> bool:
return True
[docs] def matches(self, qname: str) -> bool:
"""Match the given qname to the wildcard allowed namespaces."""
if qname == "*":
return True
namespace, tag = text.split_qname(qname)
if not self.namespaces and namespace is None:
return True
return any(self.match_namespace(ns, namespace) for ns in self.namespaces)
[docs] @staticmethod
def match_namespace(source: Optional[str], cmp: Optional[str]) -> bool:
if not source and cmp is None:
return True
if source == cmp:
return True
if source == NamespaceType.ANY:
return True
if source and source[0] == "!" and source[1:] != cmp:
return True
return False
[docs]@dataclass(frozen=True)
class XmlAttribute(XmlVar):
"""Dataclass field bind metadata for xml attributes."""
@property
def is_attribute(self) -> bool:
return True
[docs]@dataclass(frozen=True)
class XmlAttributes(XmlVar):
"""Dataclass field bind metadata for xml wildcard attributes."""
@property
def is_attributes(self) -> bool:
return True
[docs]@dataclass(frozen=True)
class XmlText(XmlVar):
"""Dataclass field bind metadata for xml text content."""
@property
def is_text(self) -> bool:
return True
[docs]class FindMode(IntEnum):
"""Find switches to be used to find a specific var."""
ALL = auto()
ATTRIBUTE = auto()
ATTRIBUTES = auto()
ELEMENT = auto()
TEXT = auto()
WILDCARD = auto()
MIXED_CONTENT = ()
NOT_WILDCARD = auto()
LIST = auto()
NOT_LIST = auto()
find_lambdas = {
FindMode.ALL: lambda x: True,
FindMode.ATTRIBUTE: lambda x: x.is_attribute,
FindMode.ATTRIBUTES: lambda x: x.is_attributes,
FindMode.ELEMENT: lambda x: x.is_element,
FindMode.TEXT: lambda x: x.is_text,
FindMode.WILDCARD: lambda x: x.is_wildcard,
FindMode.MIXED_CONTENT: lambda x: x.is_mixed_content,
FindMode.NOT_WILDCARD: lambda x: not x.is_wildcard,
FindMode.LIST: lambda x: x.is_list,
FindMode.NOT_LIST: lambda x: not x.is_list,
}