def next_node(self, element: Element, position: int, ctx: XmlContext) -> XmlNode: """ Initialize the next node to be queued for the given starting element. Search by the given element tag for a matching variable and create the next node by the variable type. :return: The next node to be queued. :raises: XmlContextError if the element is unknown and parser config is strict. """ qname = QName(element.tag) var = self.meta.find_var(qname, FindMode.NOT_WILDCARD) if not var: var = self.meta.find_var(qname, FindMode.WILDCARD) if not var: if self.config.fail_on_unknown_properties: raise XmlContextError( f"{self.meta.qname} does not support mixed content: {qname}" ) return SkipNode(position=position) if var.clazz: xsi_type = ParserUtils.parse_xsi_type(element) meta = ctx.fetch(var.clazz, self.meta.qname.namespace, xsi_type) return ElementNode(position=position, meta=meta, config=self.config) if var.is_any_type: return WildcardNode(position=position, qname=var.qname) return PrimitiveNode(position=position, var=var)
def next_node(self, element: Element, position: int, ctx: XmlContext) -> XmlNode: qname = QName(element.tag) var = self.meta.find_var(qname, FindMode.NOT_WILDCARD) if not var: var = self.meta.find_var(qname, FindMode.WILDCARD) if not var: if self.config.fail_on_unknown_properties: raise XmlContextError( f"{self.meta.qname} does not support mixed content: {qname}" ) return SkipNode(position=position) if var.clazz: xsi_type = ParserUtils.parse_xsi_type(element) meta = ctx.fetch(var.clazz, self.meta.qname.namespace, xsi_type) return ElementNode(position=position, meta=meta, default=var.default, config=self.config) if var.is_any_type: return WildcardNode(position=position, qname=var.qname) return PrimitiveNode( position=position, types=var.types, default=var.default, tokens=var.is_tokens, )
def build(self, clazz: Type, parent_ns: Optional[str] = None) -> XmlMeta: """Fetch from cache or build the metadata object for the given class and parent namespace.""" if clazz not in self.cache: # Ensure the given type is a dataclass. if not is_dataclass(clazz): raise XmlContextError(f"Object {clazz} is not a dataclass.") # Fetch the dataclass meta settings and make sure we don't inherit # the parent class meta. meta = getattr(clazz, "Meta", None) if meta and meta.__qualname__ != f"{clazz.__name__}.Meta": meta = None name = getattr(meta, "name", self.name_generator(clazz.__name__)) nillable = getattr(meta, "nillable", False) namespace = getattr(meta, "namespace", parent_ns) module = sys.modules[clazz.__module__] source_namespace = getattr(module, "__NAMESPACE__", None) self.cache[clazz] = XmlMeta( name=name, clazz=clazz, qname=QName(namespace, name), source_qname=QName(source_namespace, name), nillable=nillable, vars=list(self.get_type_hints(clazz, namespace)), ) return self.cache[clazz]
def build(self, clazz: Type, parent_ns: Optional[str] = None) -> XmlMeta: """ Fetch from cache or build the binding metadata for the given class and parent namespace. :param clazz: A dataclass type :param parent_ns: The inherited parent namespace """ if clazz not in self.cache: # Ensure the given type is a dataclass. if not is_dataclass(clazz): raise XmlContextError(f"Object {clazz} is not a dataclass.") # Fetch the dataclass meta settings and make sure we don't inherit # the parent class meta. meta = clazz.Meta if "Meta" in clazz.__dict__ else None name = getattr(meta, "name", None) or self.local_name( clazz.__name__) nillable = getattr(meta, "nillable", False) namespace = getattr(meta, "namespace", parent_ns) module = sys.modules[clazz.__module__] source_namespace = getattr(module, "__NAMESPACE__", None) self.cache[clazz] = XmlMeta( clazz=clazz, qname=build_qname(namespace, name), source_qname=build_qname(source_namespace, name), nillable=nillable, vars=list(self.get_type_hints(clazz, namespace)), ) return self.cache[clazz]
def __post_init__(self, xml_type: Optional[str]): if xml_type == XmlType.ELEMENT: self.element = True elif xml_type == XmlType.ELEMENTS: self.elements = True elif xml_type == XmlType.ATTRIBUTE: self.attribute = True self.any_type = False elif xml_type == XmlType.ATTRIBUTES: self.attributes = True self.any_type = False elif xml_type == XmlType.WILDCARD: self.wildcard = True self.any_type = False elif xml_type == XmlType.TEXT: self.text = True self.any_type = False elif xml_type: raise XmlContextError(f"Unknown xml type `{xml_type}`")
def default_xml_type(cls, clazz: Type) -> str: """ Return the default xml type for the fields of the given dataclass with an undefined type. :param clazz: A dataclass type """ counters: Dict[str, int] = defaultdict(int) for var in fields(clazz): xml_type = var.metadata.get("type") counters[xml_type or "undefined"] += 1 if counters[XmlType.TEXT] > 1: raise XmlContextError( f"Dataclass `{clazz.__name__}` includes more than one text node!" ) if counters["undefined"] == 1 and counters[XmlType.TEXT] == 0: return XmlType.TEXT return XmlType.ELEMENT
def build(self, clazz: Type, parent_ns: Optional[str] = None) -> XmlMeta: if clazz not in self.cache: if not is_dataclass(clazz): raise XmlContextError(f"Object {clazz} is not a dataclass.") meta = getattr(clazz, "Meta", None) if meta and meta.__qualname__ != f"{clazz.__name__}.Meta": meta = None name = getattr(meta, "name", self.name_generator(clazz.__name__)) nillable = getattr(meta, "nillable", False) namespace = getattr(meta, "namespace", parent_ns) module = sys.modules[clazz.__module__] source_namespace = getattr(module, "__NAMESPACE__", None) self.cache[clazz] = XmlMeta( name=name, clazz=clazz, qname=QName(namespace, name), source_qname=QName(source_namespace, name), nillable=nillable, vars=list(self.get_type_hints(clazz, namespace)), ) return self.cache[clazz]
def next_node(self, element: Element, position: int, ctx: XmlContext) -> XmlNode: raise XmlContextError("Primitive node doesn't support child nodes!")
def child(self, qname: str, attrs: Dict, ns_map: Dict, position: int) -> XmlNode: raise XmlContextError("Primitive node doesn't support child nodes!")