def test_to_xml(self): self.assertEqual(None, to_xml(None)) self.assertEqual("1", to_xml(1)) self.assertEqual("1.5", to_xml(1.5)) self.assertEqual("true", to_xml(True)) self.assertEqual("false", to_xml(False)) self.assertEqual("optional", to_xml(UseType.OPTIONAL)) self.assertEqual("INF", to_xml(float("inf"))) self.assertEqual("INF", to_xml(float("+inf"))) self.assertEqual("-INF", to_xml(float("-inf"))) self.assertEqual("NaN", to_xml(float("nan"))) self.assertEqual("INF", to_xml(Decimal("inf"))) self.assertEqual("INF", to_xml(Decimal("+inf"))) self.assertEqual("-INF", to_xml(Decimal("-inf"))) self.assertEqual("8.77683E-8", to_xml(Decimal("8.77683E-8"))) self.assertEqual("8.77683E-08", to_xml(float("8.77683E-8"))) self.assertEqual("a", to_xml(QName("a"))) self.assertEqual("a", to_xml(QName("a"))) self.assertEqual("{a}b", to_xml(QName("a", "b"))) self.assertEqual("1 2", to_xml([1, 2])) self.assertEqual("INF optional", to_xml([float("inf"), UseType.OPTIONAL])) namespaces = Namespaces() namespaces.add("a", "aa") self.assertEqual("aa:b", to_xml(QName("a", "b"), namespaces)) self.assertEqual("b", to_xml(QName("b"), namespaces)) with self.assertRaises(ConverterError): to_xml(BookForm())
def render_element_node(self, parent: Element, value: Any, var: XmlVar, namespaces: Namespaces): qname = value.qname if hasattr(value, "qname") else var.qname if isinstance(qname, QName): namespaces.add(qname.namespace) sub_element = SubElement(parent, qname) self.render_node(sub_element, value, namespaces) self.set_xsi_type(sub_element, value, var, namespaces) SerializeUtils.set_nil_attribute(sub_element, var.nillable, namespaces)
def set_attribute(element: Element, key: Any, value: Any, namespaces: Namespaces): if key == QNames.XSI_NIL and (element.text or len(element) > 0): return if isinstance(key, QName): namespaces.add(key.namespace) value = to_xml(value, namespaces) if not value: return element.set(key, value)
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.data)
def render_wildcard_node(self, parent: Element, value: Any, var: XmlVar, namespaces: Namespaces): if value.qname: sub_element = SubElement(parent, value.qname) else: sub_element = parent namespaces.add_all(value.ns_map) SerializeUtils.set_text(sub_element, value.text, namespaces) SerializeUtils.set_tail(sub_element, value.tail, namespaces) SerializeUtils.set_attributes(sub_element, value.attributes, namespaces) for child in value.children: self.render_sub_node(sub_element, child, var, namespaces) SerializeUtils.set_nil_attribute(sub_element, var.nillable, namespaces)
def render_element_node(self, parent: Element, value: Any, var: XmlVar, namespaces: Namespaces): """Render a child element for the given parent according to the field xml metadata.""" if hasattr(value, "qname"): qname = value.qname elif var.is_wildcard: meta = self.context.fetch(value.__class__, QName(parent).namespace) qname = meta.qname else: qname = var.qname namespaces.add(qname.namespace) sub_element = SubElement(parent, qname) self.render_node(sub_element, value, namespaces) self.set_xsi_type(sub_element, value, var, namespaces) SerializeUtils.set_nil_attribute(sub_element, var.nillable, namespaces)
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.data)
def render_wildcard_node(self, parent: Element, value: Any, var: XmlVar, namespaces: Namespaces): """Render a child element for the given parent according to the wildcard field metadata.""" if value.qname: sub_element = SubElement(parent, value.qname) else: sub_element = parent namespaces.add_all(value.ns_map) SerializeUtils.set_text(sub_element, value.text, namespaces) SerializeUtils.set_tail(sub_element, value.tail, namespaces) SerializeUtils.set_attributes(sub_element, value.attributes, namespaces) for child in value.children: self.render_sub_node(sub_element, child, var, namespaces) SerializeUtils.set_nil_attribute(sub_element, var.nillable, namespaces)
def render_complex_node(self, parent: Element, obj: Any, namespaces: Namespaces): meta = self.context.build(obj.__class__, QName(parent).namespace) for var, value in self.next_value(meta, obj): if value is None: continue elif var.is_attribute: SerializeUtils.set_attribute(parent, var.qname, value, namespaces) elif var.is_attributes: SerializeUtils.set_attributes(parent, value, namespaces) elif var.is_text: namespaces.add(var.qname.namespace) SerializeUtils.set_text(parent, value, namespaces) elif isinstance(value, list): self.render_sub_nodes(parent, value, var, namespaces) else: self.render_sub_node(parent, value, var, namespaces) SerializeUtils.set_nil_attribute(parent, meta.nillable, namespaces)
def setUp(self): super().setUp() self.serializer = XmlSerializer(pretty_print=True) self.namespaces = Namespaces() self.books = Books(book=[ BookForm( id="bk001", author="Hightower, Kim", title="The First Book", genre="Fiction", price=44.95, pub_date="2000-10-01", review="An amazing story of nothing.", ), BookForm( id="bk002", author="Nagata, Suanne", title="Becoming Somebody", genre="Biography", review="A masterpiece of the fine art of gossiping.", ), ])
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) self.assertEqual("b", namespaces.prefix("bar")) self.assertEqual("xs", namespaces.prefix("http://www.w3.org/2001/XMLSchema")) self.assertIsNone(namespaces.prefix("http://xsdata"))
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 test_property_ns_map(self): namespaces = Namespaces() namespaces.add_all({ "": "xsdata", "foo": "http://www.w3.org/2001/XMLSchema-instance", "b": "bar", }) namespaces.add("bar", "again") namespaces.add("one") namespaces.add("two") expected = { None: "xsdata", "b": "bar", "again": "bar", "ns4": "one", "ns5": "two", "xsi": "http://www.w3.org/2001/XMLSchema-instance", } self.assertEqual(expected, namespaces.ns_map)
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.context.build(obj.__class__) namespaces = namespaces or Namespaces() namespaces.register() namespaces.add(meta.qname.namespace) root = Element(meta.qname) self.render_node(root, obj, namespaces) cleanup_namespaces(root, top_nsmap=namespaces.ns_map, keep_ns_prefixes=namespaces.prefixes) return root
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.context.build(obj.__class__) namespaces = namespaces or Namespaces() namespaces.register() prefix = DEFAULT_NS_PREFIX if meta.element_form == FormType.QUALIFIED else None namespaces.add(meta.qname.namespace, prefix=prefix) root = Element(meta.qname, nsmap=namespaces.ns_map) self.render_node(root, obj, namespaces) cleanup_namespaces(root, top_nsmap=namespaces.ns_map, keep_ns_prefixes=namespaces.prefixes) return root
def setUp(self) -> None: super().setUp() self.namespaces = Namespaces() self.element = Element("root")
class XmlSerializerTests(TestCase): def setUp(self): super(XmlSerializerTests, self).setUp() self.serializer = XmlSerializer(pretty_print=True) self.namespaces = Namespaces() self.books = Books( book=[ BookForm( id="bk001", author="Hightower, Kim", title="The First Book", genre="Fiction", price=44.95, pub_date="2000-10-01", review="An amazing story of nothing.", ), BookForm( id="bk002", author="Nagata, Suanne", title="Becoming Somebody", genre="Biography", review="A masterpiece of the fine art of gossiping.", ), ] ) def test_render(self): actual = self.serializer.render(self.books) expected = ( "<?xml version='1.0' encoding='UTF-8'?>\n" '<ns0:books xmlns:ns0="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" "</ns0:books>\n" ) self.assertEqual(expected, actual) def test_render_with_provided_namespaces(self): self.namespaces.add("urn:books", "burn") actual = self.serializer.render(self.books, self.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_render_no_dataclass(self): with self.assertRaises(XmlContextError) as cm: self.serializer.render(self) self.assertEqual( f"Object {self.__class__} is not a dataclass.", str(cm.exception) ) @mock.patch.object(SerializeUtils, "set_nil_attribute") @mock.patch.object(SerializeUtils, "set_text") @mock.patch.object(SerializeUtils, "set_attributes") @mock.patch.object(SerializeUtils, "set_attribute") @mock.patch.object(XmlSerializer, "render_sub_node") @mock.patch.object(XmlSerializer, "next_value") def test_render_node( self, mock_next_value, mock_render_sub_node, mock_set_attribute, mock_set_attributes, mock_set_text, mock_set_nil_attribute, ): root = Element("root") prod_meta = self.serializer.context.build(ProductType) size_meta = self.serializer.context.build(SizeType) obj = ProductType() attribute = prod_meta.find_var("effDate") attributes = prod_meta.find_var("{!}other_attributes") text = replace(size_meta.find_var("value"), qname=QName("foo", "bar")) sub_node = prod_meta.find_var("name") mock_next_value.return_value = [ (attribute, None), (attribute, 1), (attributes, dict(a=1)), (text, "txt"), (sub_node, 1), (sub_node, [2, 3]), ] self.serializer.render_node(root, obj, self.namespaces) self.assertEqual({"ns0": "foo"}, self.namespaces.ns_map) mock_set_attribute.assert_called_once_with( root, attribute.qname, 1, self.namespaces ) mock_set_attributes.assert_called_once_with(root, dict(a=1), self.namespaces) mock_set_text.assert_called_once_with(root, "txt", self.namespaces) mock_render_sub_node.assert_has_calls( [ mock.call(root, 1, sub_node, self.namespaces), mock.call(root, 2, sub_node, self.namespaces), mock.call(root, 3, sub_node, self.namespaces), ] ) mock_set_nil_attribute.assert_called_once_with(root, False, self.namespaces) def test_render_node_without_dataclass(self): root = Element("root") self.serializer.render_node(root, 1, self.namespaces) self.assertEqual("1", root.text) @mock.patch.object(XmlSerializer, "render_sub_node") def test_render_sub_nodes(self, mock_render_sub_node): root = Element("root") meta = self.serializer.context.build(ProductType) var = meta.find_var("number") self.serializer.render_sub_nodes(root, [1, 2, 3], var, self.namespaces) self.assertEqual(3, mock_render_sub_node.call_count) mock_render_sub_node.assert_has_calls( [ mock.call(root, 1, var, self.namespaces), mock.call(root, 2, var, self.namespaces), mock.call(root, 3, var, self.namespaces), ] ) @mock.patch.object(XmlSerializer, "render_wildcard_node") def test_render_sub_node_with_generic_object(self, mock_render_wildcard_node): root = Element("root") value = AnyElement() meta = self.serializer.context.build(DescriptionType) var = meta.find_var(mode=FindMode.WILDCARD) self.serializer.render_sub_node(root, value, var, self.namespaces) self.assertEqual(1, mock_render_wildcard_node.call_count) mock_render_wildcard_node.assert_called_once_with( root, value, var, self.namespaces ) @mock.patch.object(XmlSerializer, "render_element_node") def test_render_sub_node_with_xml_element(self, mock_render_element_node): root = Element("root") value = 1 meta = self.serializer.context.build(ProductType) var = meta.find_var("number") self.serializer.render_sub_node(root, value, var, self.namespaces) self.assertEqual(1, mock_render_element_node.call_count) mock_render_element_node.assert_called_once_with( root, value, var, self.namespaces ) @mock.patch.object(XmlSerializer, "render_element_node") def test_render_sub_node_with_dataclass_object(self, mock_render_element_node): root = Element("root") value = SizeType() meta = self.serializer.context.build(DescriptionType) var = meta.find_var(mode=FindMode.WILDCARD) self.serializer.render_sub_node(root, value, var, self.namespaces) self.assertEqual(1, mock_render_element_node.call_count) mock_render_element_node.assert_called_once_with( root, value, var, self.namespaces ) @mock.patch.object(SerializeUtils, "set_tail") @mock.patch.object(SerializeUtils, "set_text") def test_render_sub_node_with_primitive_value_and_not_xml_element( self, mock_set_text, mock_set_tail ): root = Element("root") value = 1 meta = self.serializer.context.build(DescriptionType) var = meta.find_var(mode=FindMode.WILDCARD) self.serializer.render_sub_node(root, value, var, self.namespaces) self.assertEqual(1, mock_set_text.call_count) mock_set_text.assert_called_once_with(root, value, self.namespaces) root.text = "foo" self.serializer.render_sub_node(root, value, var, self.namespaces) self.assertEqual(1, mock_set_tail.call_count) mock_set_tail.assert_called_once_with(root, value, self.namespaces) @mock.patch.object(SerializeUtils, "set_nil_attribute") @mock.patch.object(XmlSerializer, "set_xsi_type") @mock.patch.object(XmlSerializer, "render_node") def test_render_element_node( self, mock_render_node, mock_set_xsi_type, mock_set_nil_attribute ): root = Element("root") value = SizeType() meta = self.serializer.context.build(DescriptionType) var = meta.find_var(mode=FindMode.WILDCARD) self.serializer.render_element_node(root, value, var, self.namespaces) child = root[0] mock_render_node.assert_called_once_with(child, value, self.namespaces) mock_set_xsi_type.assert_called_once_with(child, value, var, self.namespaces) mock_set_nil_attribute.assert_called_once_with( child, var.nillable, self.namespaces ) self.assertEqual(var.qname, child.tag) self.assertEqual( {"ns0": "http://www.w3.org/1999/xhtml"}, self.namespaces.ns_map ) @mock.patch.object(SerializeUtils, "set_nil_attribute") @mock.patch.object(XmlSerializer, "render_node") def test_render_element_node_with_specific_qname( self, mock_render_node, mock_set_nil_attribute ): root = Element("root") value = SizeType() value.qname = "foo" meta = self.serializer.context.build(DescriptionType) var = meta.find_var(mode=FindMode.WILDCARD) self.serializer.render_element_node(root, value, var, self.namespaces) child = root[0] mock_render_node.assert_called_once_with(child, value, self.namespaces) mock_set_nil_attribute.assert_called_once_with( child, var.nillable, self.namespaces ) self.assertEqual("foo", child.tag) self.assertEqual(0, len(self.namespaces.ns_map)) @mock.patch.object(XmlSerializer, "render_sub_node") @mock.patch.object(SerializeUtils, "set_nil_attribute") @mock.patch.object(SerializeUtils, "set_attributes") @mock.patch.object(SerializeUtils, "set_tail") @mock.patch.object(SerializeUtils, "set_text") def test_render_wildcard_node( self, mock_set_text, mock_set_tail, mock_set_attributes, mock_set_nil_attribute, mock_render_sub_node, ): root = Element("root") value = AnyElement( text="foo", tail="bar", attributes=dict(a=1), children=[AnyElement(), AnyElement()], ns_map={"foo": "bar"}, qname="foo", ) meta = self.serializer.context.build(DescriptionType) var = meta.find_var(mode=FindMode.WILDCARD) self.serializer.render_wildcard_node(root, value, var, self.namespaces) child = root[0] self.assertEqual({"foo": "bar"}, self.namespaces.ns_map) self.assertEqual(value.qname, child.tag) mock_set_text.assert_called_once_with(child, value.text, self.namespaces) mock_set_tail.assert_called_once_with(child, value.tail, self.namespaces) mock_set_attributes.assert_called_once_with( child, value.attributes, self.namespaces ) mock_render_sub_node.assert_has_calls( [ mock.call(child, value.children[0], var, self.namespaces), mock.call(child, value.children[1], var, self.namespaces), ] ) mock_set_nil_attribute.assert_called_once_with( child, var.nillable, self.namespaces ) @mock.patch.object(XmlSerializer, "render_sub_node") @mock.patch.object(SerializeUtils, "set_nil_attribute") @mock.patch.object(SerializeUtils, "set_attributes") @mock.patch.object(SerializeUtils, "set_tail") @mock.patch.object(SerializeUtils, "set_text") def test_render_wildcard_node_without_qname( self, mock_set_text, mock_set_tail, mock_set_attributes, mock_set_nil_attribute, mock_render_sub_node, ): root = Element("root") value = AnyElement( text="foo", tail="bar", attributes=dict(a=1), children=[AnyElement()] ) meta = self.serializer.context.build(DescriptionType) var = meta.find_var(mode=FindMode.WILDCARD) self.serializer.render_wildcard_node(root, value, var, self.namespaces) self.assertEqual(0, len(self.namespaces.ns_map)) self.assertEqual(0, len(root)) mock_set_text.assert_called_once_with(root, value.text, self.namespaces) mock_set_tail.assert_called_once_with(root, value.tail, self.namespaces) mock_set_attributes.assert_called_once_with( root, value.attributes, self.namespaces ) mock_render_sub_node.assert_called_once_with( root, value.children[0], var, self.namespaces ) mock_set_nil_attribute.assert_called_once_with( root, var.nillable, self.namespaces ) def test_set_xsi_type_with_non_dataclass(self): elem = Element("foo") value = 1 meta = self.serializer.context.build(ProductType) var = meta.find_var("number") self.serializer.set_xsi_type(elem, value, var, self.namespaces) self.assertNotIn(QNames.XSI_TYPE, elem.attrib) def test_set_xsi_type_when_value_type_matches_var_clazz(self): elem = Element("foo") value = SizeType() meta = self.serializer.context.build(ProductType) var = meta.find_var("size") self.serializer.set_xsi_type(elem, value, var, self.namespaces) self.assertNotIn(QNames.XSI_TYPE, elem.attrib) def test_set_xsi_type_when_value_is_not_derived_from_var_clazz(self): elem = Element("foo") value = ColorType() meta = self.serializer.context.build(ProductType) var = meta.find_var("size") with self.assertRaises(SerializerError) as cm: self.serializer.set_xsi_type(elem, value, var, self.namespaces) self.assertEqual("ColorType is not derived from SizeType", str(cm.exception)) @mock.patch.object(XmlContext, "is_derived", return_value=True) def test_set_xsi_type_when_value_is_derived_from_var_clazz(self, *args): elem = Element("foo") value = Items() meta = self.serializer.context.build(ProductType) items_meta = self.serializer.context.build(Items) var = meta.find_var("size") self.serializer.set_xsi_type(elem, value, var, self.namespaces) self.assertEqual(items_meta.source_qname, elem.attrib[QNames.XSI_TYPE]) def test_next_value(self): @dataclass class A: x0: Optional[int] = field(default=None) x1: List[int] = field( default_factory=list, metadata=dict(type="Element", sequential=True) ) x2: List[int] = field( default_factory=list, metadata=dict(type="Element", sequential=True) ) x3: Optional[int] = field(default=None) obj = A(x0=1, x1=[2, 3, 4], x2=[6, 7], x3=8) meta = self.serializer.context.build(A) x0 = meta.find_var("x0") x1 = meta.find_var("x1") x2 = meta.find_var("x2") x3 = meta.find_var("x3") actual = self.serializer.next_value(meta, obj) expected = [ (x0, 1), (x1, 2), (x2, 6), (x1, 3), (x2, 7), (x1, 4), (x3, 8), ] self.assertIsInstance(actual, Iterator) self.assertEqual(expected, list(actual))
def qname_to_xml(qname: QName, namespaces: Namespaces) -> str: namespaces.add(qname.namespace) prefix = namespaces.prefix(qname.namespace) return f"{prefix}:{qname.localname}" if prefix else qname.localname
def set_nil_attribute(element: Element, nillable: bool, namespaces: Namespaces): """Set element xs:nil attribute if necessary.""" 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")
def test_register(self): namespaces = Namespaces() namespaces.add(Namespace.XML.uri) 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) namespaces.unregister() element = Element("{http://komposta.net}root") self.assertEqual({"ns0": "http://komposta.net"}, element.nsmap)
def setUp(self) -> None: super(SerializeUtilsTests, self).setUp() self.namespaces = Namespaces() self.element = Element("root")