def test_render_with_provided_namespaces(self): serializer = XmlSerializer(pretty_print=True) namespaces = Namespaces() namespaces.add("urn:books", "burn") actual = serializer.render(self.books, namespaces) expected = ( "<?xml version='1.0' encoding='UTF-8'?>\n" '<burn:books xmlns:burn="urn:books">\n' ' <book id="bk001" lang="en">\n' " <author>Hightower, Kim</author>\n" " <title>The First Book</title>\n" " <genre>Fiction</genre>\n" " <price>44.95</price>\n" " <pub_date>2000-10-01</pub_date>\n" " <review>An amazing story of nothing.</review>\n" " </book>\n" ' <book id="bk002" lang="en">\n' " <author>Nagata, Suanne</author>\n" " <title>Becoming Somebody</title>\n" " <genre>Biography</genre>\n" " <review>A masterpiece of the fine art of gossiping.</review>\n" " </book>\n" "</burn:books>\n") self.assertEqual(expected, actual)
def test_property_prefixes(self): namespaces = Namespaces() namespaces.add_all({ "b": "bar", None: "http://www.w3.org/2001/XMLSchema", "foo": "http://www.w3.org/2001/XMLSchema-instance", }) self.assertEqual(["b", "xs", "xsi"], namespaces.prefixes)
def test_add_all(self): namespaces = Namespaces() namespaces.add_all({ "b": "bar", None: "http://www.w3.org/2001/XMLSchema", "foo": "http://www.w3.org/2001/XMLSchema-instance", }) expected = { "bar": {"b"}, "http://www.w3.org/2001/XMLSchema": {"xs"}, "http://www.w3.org/2001/XMLSchema-instance": {"xsi"}, } self.assertEqual(expected, namespaces.items)
def test_add(self): namespaces = Namespaces() namespaces.add("foo") namespaces.add("foo") namespaces.add("bar", "one") namespaces.add("bar", "two") namespaces.add(Namespace.XSI.uri, "a") namespaces.add(Namespace.XSI.uri, "b") namespaces.add(Namespace.XS.uri, "c") namespaces.add(Namespace.XS.uri, "d") expected = { "bar": {"one", "two"}, "foo": {"ns0"}, "http://www.w3.org/2001/XMLSchema": {"xs"}, "http://www.w3.org/2001/XMLSchema-instance": {"xsi"}, } self.assertEqual(expected, namespaces.items)
def test_register(self): namespaces = Namespaces() namespaces.add("http://komposta.net", "bar") namespaces.add("http://foobar", "ns2") # ns{\d} are not registered element = Element("{http://komposta.net}root") self.assertEqual({"ns0": "http://komposta.net"}, element.nsmap) namespaces.register() element = Element("{http://komposta.net}root") self.assertEqual({"bar": "http://komposta.net"}, element.nsmap) element = Element("{http://foobar}root") self.assertEqual({"ns0": "http://foobar"}, element.nsmap)
def test_clear(self): namespaces = Namespaces() namespaces.add_all({ "b": "bar", "foo": "http://www.w3.org/2001/XMLSchema-instance" }) self.assertEqual(2, len(namespaces.ns_map)) namespaces.clear() self.assertEqual(0, len(namespaces.ns_map))
def render_sub_nodes( self, parent, values: Any, var: ClassVar, namespaces: Namespaces ): if not isinstance(values, list): values = [values] is_wildcard = var.is_any_element for value in values: if value is None: continue elif not is_wildcard: sub_element = SubElement(parent, var.qname) self.render_node(value, sub_element, namespaces) self.set_nil_attribute(sub_element, var.nillable, namespaces) elif isinstance(value, str): if parent.text: self.set_tail(parent, value) else: self.set_text(parent, value) elif isinstance(value, AnyElement): if value.qname: sub_element = SubElement(parent, value.qname) else: sub_element = parent namespaces.add_all(value.ns_map) self.set_text(sub_element, value.text) self.set_tail(sub_element, value.tail) self.set_attributes(sub_element, value.attributes) for child in value.children: self.render_sub_nodes(sub_element, child, var, namespaces) self.set_nil_attribute(parent, var.nillable, namespaces) else: sub_element = SubElement(parent, value.qname) self.render_node(value, sub_element, namespaces) self.set_nil_attribute(sub_element, var.nillable, namespaces)
def render_node(self, obj, parent, namespaces: Namespaces) -> Element: """Recursively traverse the given dataclass instance fields and build the lxml Element structure.""" if not is_dataclass(obj): self.set_text(parent, obj) return parent meta = self.class_meta(obj.__class__, QName(parent).namespace) for var, value in self.next_value(meta, obj): if value is not None: if not var.is_any_element and not var.is_any_attribute: namespaces.add(var.namespace) if var.is_attribute: self.set_attribute(parent, var.qname, value) elif var.is_any_attribute: self.set_attributes(parent, value) elif var.is_text: self.set_text(parent, value) else: self.render_sub_nodes(parent, value, var, namespaces) self.set_nil_attribute(parent, meta.nillable, namespaces) return parent
def render_tree(self, obj: Any, namespaces: Optional[Namespaces] = None) -> Element: """ Convert a dataclass instance to a nested Element structure. Optionally provide a namespaces instance with a predefined list of namespace uris and prefixes. """ meta = self.class_meta(obj.__class__) namespaces = namespaces or Namespaces() namespaces.register() namespaces.add(meta.namespace) root = self.render_node(obj, Element(meta.qname), namespaces) cleanup_namespaces( root, top_nsmap=namespaces.ns_map, keep_ns_prefixes=namespaces.prefixes ) return root
def test_property_ns_map(self): namespaces = Namespaces() namespaces.add_all({ "b": "bar", None: "http://www.w3.org/2001/XMLSchema", "foo": "http://www.w3.org/2001/XMLSchema-instance", }) namespaces.add("bar", "again") namespaces.add("one") namespaces.add("two") expected = { "b": "bar", "again": "bar", "ns0": "one", "ns1": "two", "xs": "http://www.w3.org/2001/XMLSchema", "xsi": "http://www.w3.org/2001/XMLSchema-instance", } self.assertEqual(expected, namespaces.ns_map)
def set_nil_attribute( cls, element: Element, nillable: bool, namespaces: Namespaces ): if nillable and element.text is None and len(element) == 0: namespaces.add(Namespace.XSI.uri, Namespace.XSI.prefix) element.set(QNames.XSI_NIL, "true")