From c9548da6996e1dfced451fbf63b5e4bffd821802 Mon Sep 17 00:00:00 2001 From: Davide Brunato Date: Mon, 27 Mar 2023 17:45:58 +0200 Subject: [PATCH] Add a test with a resolution for issue #341 --- .../issues/issue_341/issue_341-ext.xsd | 46 ++++++++++ .../test_cases/issues/issue_341/issue_341.xml | 8 ++ .../test_cases/issues/issue_341/issue_341.xsd | 42 +++++++++ tests/validation/test_decoding.py | 92 ++++++++++++++++++- 4 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 tests/test_cases/issues/issue_341/issue_341-ext.xsd create mode 100644 tests/test_cases/issues/issue_341/issue_341.xml create mode 100644 tests/test_cases/issues/issue_341/issue_341.xsd diff --git a/tests/test_cases/issues/issue_341/issue_341-ext.xsd b/tests/test_cases/issues/issue_341/issue_341-ext.xsd new file mode 100644 index 00000000..04609d51 --- /dev/null +++ b/tests/test_cases/issues/issue_341/issue_341-ext.xsd @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/test_cases/issues/issue_341/issue_341.xml b/tests/test_cases/issues/issue_341/issue_341.xml new file mode 100644 index 00000000..ecaf9eb9 --- /dev/null +++ b/tests/test_cases/issues/issue_341/issue_341.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/tests/test_cases/issues/issue_341/issue_341.xsd b/tests/test_cases/issues/issue_341/issue_341.xsd new file mode 100644 index 00000000..10f8017f --- /dev/null +++ b/tests/test_cases/issues/issue_341/issue_341.xsd @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/validation/test_decoding.py b/tests/validation/test_decoding.py index ebe3cdec..da5ddace 100644 --- a/tests/validation/test_decoding.py +++ b/tests/validation/test_decoding.py @@ -30,7 +30,7 @@ from xmlschema.names import XSD_STRING, XSI_NIL from xmlschema.converters import UnorderedConverter -from xmlschema.validators import XMLSchema11 +from xmlschema.validators import XMLSchema11, ModelVisitor from xmlschema.testing import XsdValidatorTestCase, etree_elements_assert_equal VEHICLES_DICT = { @@ -375,7 +375,9 @@ def test_to_dict_from_etree(self): xml_dict = self.vh_schema.to_dict(vh_xml_tree, namespaces=self.vh_namespaces) self.assertEqual(xml_dict, VEHICLES_DICT) - xml_dict = xmlschema.to_dict(vh_xml_tree, self.vh_schema.url, namespaces=self.vh_namespaces) + xml_dict = xmlschema.to_dict( + vh_xml_tree, self.vh_schema.url, namespaces=self.vh_namespaces + ) self.assertEqual(xml_dict, VEHICLES_DICT) xml_dict = self.col_schema.to_dict(col_xml_tree) @@ -1375,6 +1377,92 @@ def test_mixed_content_decode__issue_334(self): body_text = result['Demonstrative_Examples']['Demonstrative_Example'][0]['Body_Text'] self.assertListEqual(body_text, expected) + def test_fill_missing_elements__issue_341(self): + xsd_file = self.casepath('issues/issue_341/issue_341.xsd') + xml_file = self.casepath('issues/issue_341/issue_341.xml') + schema = self.schema_class(xsd_file) + + expected = {'TEST_EL': [ + {'@Date': '2022-10-03', + 'TEST_EL_2': { + 'exists_in_xml': { + '@test_attr': 'test_value_attr', '@test_attr_2': None + } + }} + ]} + xml_dict = schema.decode(xml_file, fill_missing=True) + self.assertDictEqual(xml_dict, expected) + + def fill_missing_content(element_data: ElementData, _xsd_element, xsd_type): + group = xsd_type.model_group + if group is None: + return element_data # an element with simple content + + filled_content = [] + model = ModelVisitor(group) + xsd_element = None + for name, value, xsd_element in element_data.content: + if isinstance(name, int) or xsd_element is None: + filled_content.append((name, value, xsd_element)) + continue + + while model.element is not None: + if model.element is xsd_element: + filled_content.append((name, value, xsd_element)) + for _err in model.advance(True): + pass + break + + if model.element.max_occurs != 0: + filled_content.append((model.element.name, None, model.element)) + for _err in model.advance(False): + pass + else: + filled_content.append((name, value, xsd_element)) + + while model.element is not None: + if model.element is not xsd_element and model.element.max_occurs != 0: + filled_content.append((model.element.name, None, model.element)) + for _err in model.advance(False): + pass + + return ElementData( + element_data.tag, + element_data.text, + filled_content, + element_data.attributes + ) + + expected = {'TEST_EL': [ + {'@Date': '2022-10-03', + 'TEST_EL_2': { + 'exists_in_xml': { + '@test_attr': 'test_value_attr', '@test_attr_2': None + }, + 'not_exists_in_xml': None + }} + ]} + xml_dict = schema.decode(xml_file, element_hook=fill_missing_content, fill_missing=True) + self.assertDictEqual(xml_dict, expected) + + # Resolving more complex schemas requires more checks in hook function + xsd_file = self.casepath('issues/issue_341/issue_341-ext.xsd') + schema = self.schema_class(xsd_file) + + expected = {'TEST_EL': [ + {'@Date': '2022-10-03', + 'TEST_EL_2': { + 'exists_in_xml': { + '@test_attr': 'test_value_attr', '@test_attr_2': None + }, + 'not_exists_in_xml': None, + 'choice_elem1': None, + 'choice_elem2': None, # this is wrong (at most one element for a choice) + }} + ]} + xml_dict = schema.decode(xml_file, element_hook=fill_missing_content, fill_missing=True) + self.assertDictEqual(xml_dict, expected) + class TestDecoding11(TestDecoding): schema_class = XMLSchema11