From ae4a768215e91509d4843191ef08bbea4fd70e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Tue, 19 Dec 2023 13:17:49 +0100 Subject: [PATCH] adapter.xml: verify declared namespaces before deserializing Previously, if the elements of an XML document are part of an unknown namespace, this would lead to cryptic error messages such as: Unexpected top-level list aas:assetAdministrationShells on line 3 where, the correct expected element is indeed aas:assetAdministrationShells, leaving the user wondering about what could possibly be wrong. The only difference is the namespace, which isn't part of the error message, because it gets replaced by the prefix. To improve the error messages in this case, a check that compares the namespaces declared on the document against the ones required by the deserialization, and errors if a required namespace isn't declared. Partially fix https://github.com/eclipse-basyx/basyx-python-sdk/issues/190 --- basyx/aas/adapter/xml/xml_deserialization.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/basyx/aas/adapter/xml/xml_deserialization.py b/basyx/aas/adapter/xml/xml_deserialization.py index e42a9a6f6..73f91ced5 100644 --- a/basyx/aas/adapter/xml/xml_deserialization.py +++ b/basyx/aas/adapter/xml/xml_deserialization.py @@ -49,11 +49,12 @@ import enum from typing import Any, Callable, Dict, IO, Iterable, Optional, Set, Tuple, Type, TypeVar -from .._generic import XML_NS_AAS, MODELLING_KIND_INVERSE, ASSET_KIND_INVERSE, KEY_TYPES_INVERSE, \ +from .._generic import XML_NS_MAP, XML_NS_AAS, MODELLING_KIND_INVERSE, ASSET_KIND_INVERSE, KEY_TYPES_INVERSE, \ ENTITY_TYPES_INVERSE, IEC61360_DATA_TYPES_INVERSE, IEC61360_LEVEL_TYPES_INVERSE, KEY_TYPES_CLASSES_INVERSE, \ REFERENCE_TYPES_INVERSE, DIRECTION_INVERSE, STATE_OF_EVENT_INVERSE, QUALIFIER_KIND_INVERSE NS_AAS = XML_NS_AAS +REQUIRED_NAMESPACES: Set[str] = {XML_NS_MAP["aas"]} logger = logging.getLogger(__name__) @@ -1196,13 +1197,22 @@ def _parse_xml_document(file: IO, failsafe: bool = True, **parser_kwargs: Any) - parser = etree.XMLParser(remove_blank_text=True, remove_comments=True, **parser_kwargs) try: - return etree.parse(file, parser).getroot() + root = etree.parse(file, parser).getroot() except etree.XMLSyntaxError as e: if failsafe: logger.error(e) return None raise e + missing_namespaces: Set[str] = REQUIRED_NAMESPACES - set(root.nsmap.values()) + if missing_namespaces: + error_message = f"The following required namespaces are not declared: {' | '.join(missing_namespaces)}" \ + + " - Is the input document of an older version?" + if not failsafe: + raise KeyError(error_message) + logger.error(error_message) + return root + def _select_decoder(failsafe: bool, stripped: bool, decoder: Optional[Type[AASFromXmlDecoder]]) \ -> Type[AASFromXmlDecoder]: