diff --git a/pydantic_xml/element/element.py b/pydantic_xml/element/element.py index 4a99e30..6e82093 100644 --- a/pydantic_xml/element/element.py +++ b/pydantic_xml/element/element.py @@ -116,6 +116,12 @@ def apply_snapshot(self, snapshot: 'XmlElement[Any]') -> None: Applies a snapshot to the current element. """ + @abc.abstractmethod + def step_forward(self) -> None: + """ + Increment the current element index. + """ + @abc.abstractmethod def to_native(self) -> Any: """ @@ -305,6 +311,7 @@ def create_snapshot(self) -> 'XmlElement[NativeElement]': attributes=dict(self._state.attrib) if self._state.attrib is not None else None, elements=[element.create_snapshot() for element in self._state.elements], nsmap=dict(self._nsmap) if self._nsmap is not None else None, + sourceline=self._sourceline, ) element._state.next_element_idx = self._state.next_element_idx @@ -319,6 +326,9 @@ def apply_snapshot(self, snapshot: 'XmlElement[NativeElement]') -> None: self._state.elements = snapshot._state.elements self._state.next_element_idx = snapshot._state.next_element_idx + def step_forward(self) -> None: + self._state.next_element_idx += 1 + def is_empty(self) -> bool: if not self._state.text and not self._state.tail and not self._state.attrib and len(self._state.elements) == 0: return True diff --git a/pydantic_xml/serializers/factories/union.py b/pydantic_xml/serializers/factories/union.py index d1e4136..58e94e0 100644 --- a/pydantic_xml/serializers/factories/union.py +++ b/pydantic_xml/serializers/factories/union.py @@ -117,6 +117,7 @@ def deserialize( last_error = e if last_error is not None: + element.step_forward() raise last_error return result diff --git a/tests/test_errors.py b/tests/test_errors.py index a72fdc3..d23dd82 100644 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -170,3 +170,50 @@ class TestModel(BaseXmlModel, tag='model'): }, }, ] + + +def test_models_union_errors(): + class TestSubModel1(BaseXmlModel, tag='submodel1'): + data: int + + class TestSubModel2(BaseXmlModel, tag='submodel2'): + data: float + + class TestModel(BaseXmlModel, tag='model'): + submodel: List[Union[TestSubModel1, TestSubModel2]] + + xml = ''' + + a + b + + ''' + + with pytest.raises(pd.ValidationError) as exc: + TestModel.from_xml(xml) + + err = exc.value + assert err.title == 'TestModel' + assert err.error_count() == 2 + assert err.errors() == [ + { + 'input': 'a', + 'loc': ('submodel', 0, 'data'), + 'msg': f'[line {fmt_sourceline(3)}]: Input should be a valid number, unable to parse string as a number', + 'type': 'float_parsing', + 'ctx': { + 'orig': 'Input should be a valid number, unable to parse string as a number', + 'sourceline': fmt_sourceline(3), + }, + }, + { + 'input': 'b', + 'loc': ('submodel', 1, 'data'), + 'msg': f'[line {fmt_sourceline(4)}]: Input should be a valid integer, unable to parse string as an integer', + 'type': 'int_parsing', + 'ctx': { + 'orig': 'Input should be a valid integer, unable to parse string as an integer', + 'sourceline': fmt_sourceline(4), + }, + }, + ]