Beispiel #1
0
    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)
Beispiel #2
0
    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,
        )
Beispiel #3
0
    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]
Beispiel #4
0
    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]
Beispiel #5
0
 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}`")
Beispiel #6
0
    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
Beispiel #7
0
    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]
Beispiel #8
0
 def next_node(self, element: Element, position: int,
               ctx: XmlContext) -> XmlNode:
     raise XmlContextError("Primitive node doesn't support child nodes!")
Beispiel #9
0
 def child(self, qname: str, attrs: Dict, ns_map: Dict,
           position: int) -> XmlNode:
     raise XmlContextError("Primitive node doesn't support child nodes!")