From cd92d2083b7ec015544798451337b6d283dc5df9 Mon Sep 17 00:00:00 2001 From: Marko Ristin Date: Sun, 7 Jul 2024 12:47:07 +0200 Subject: [PATCH] Check explicitly for invalid model type in Python (#510) We explicitly check in the generated Python code that the concrete classes without descendants which have a ``modelType`` defined in the de-serialization for backwards compatibility also specify a valid and expected ``modelType`` in JSON de-serialization. --- .../python/jsonization/_generate.py | 10 +- .../expected_output/jsonization.py | 153 ++++++++++++++++-- 2 files changed, 145 insertions(+), 18 deletions(-) diff --git a/aas_core_codegen/python/jsonization/_generate.py b/aas_core_codegen/python/jsonization/_generate.py index 69e2465d..9f8a741a 100644 --- a/aas_core_codegen/python/jsonization/_generate.py +++ b/aas_core_codegen/python/jsonization/_generate.py @@ -716,12 +716,20 @@ def _generate_concrete_class_from_jsonable( # through a dispatching function, which will innately check for model type, so we # do not have to repeat the check here. if len(cls.concrete_descendants) == 0 and cls.serialization.with_model_type: + expected_model_type = naming.json_model_type(cls.name) blocks.append( Stripped( f"""\ -if 'modelType' not in jsonable: +model_type = jsonable.get("modelType", None) +if model_type is None: {I}raise DeserializationException( {II}"Expected the property modelType, but found none" +{I}) + +if model_type != {python_common.string_literal(expected_model_type)}: +{I}raise DeserializationException( +{II}f"Invalid modelType, expected '{expected_model_type}', " +{II}f"but got: {{model_type!r}}" {I})""" ) ) diff --git a/test_data/python/test_main/aas_core_meta.v3/expected_output/jsonization.py b/test_data/python/test_main/aas_core_meta.v3/expected_output/jsonization.py index 0111e7bf..a704e24c 100644 --- a/test_data/python/test_main/aas_core_meta.v3/expected_output/jsonization.py +++ b/test_data/python/test_main/aas_core_meta.v3/expected_output/jsonization.py @@ -1443,11 +1443,18 @@ def asset_administration_shell_from_jsonable( setter = _SetterForAssetAdministrationShell() - if 'modelType' not in jsonable: + model_type = jsonable.get("modelType", None) + if model_type is None: raise DeserializationException( "Expected the property modelType, but found none" ) + if model_type != 'AssetAdministrationShell': + raise DeserializationException( + f"Invalid modelType, expected 'AssetAdministrationShell', " + f"but got: {model_type!r}" + ) + for key, jsonable_value in jsonable.items(): setter_method = ( _SETTER_MAP_FOR_ASSET_ADMINISTRATION_SHELL.get(key) @@ -2296,11 +2303,18 @@ def submodel_from_jsonable( setter = _SetterForSubmodel() - if 'modelType' not in jsonable: + model_type = jsonable.get("modelType", None) + if model_type is None: raise DeserializationException( "Expected the property modelType, but found none" ) + if model_type != 'Submodel': + raise DeserializationException( + f"Invalid modelType, expected 'Submodel', " + f"but got: {model_type!r}" + ) + for key, jsonable_value in jsonable.items(): setter_method = ( _SETTER_MAP_FOR_SUBMODEL.get(key) @@ -3200,11 +3214,18 @@ def submodel_element_list_from_jsonable( setter = _SetterForSubmodelElementList() - if 'modelType' not in jsonable: + model_type = jsonable.get("modelType", None) + if model_type is None: raise DeserializationException( "Expected the property modelType, but found none" ) + if model_type != 'SubmodelElementList': + raise DeserializationException( + f"Invalid modelType, expected 'SubmodelElementList', " + f"but got: {model_type!r}" + ) + for key, jsonable_value in jsonable.items(): setter_method = ( _SETTER_MAP_FOR_SUBMODEL_ELEMENT_LIST.get(key) @@ -3578,11 +3599,18 @@ def submodel_element_collection_from_jsonable( setter = _SetterForSubmodelElementCollection() - if 'modelType' not in jsonable: + model_type = jsonable.get("modelType", None) + if model_type is None: raise DeserializationException( "Expected the property modelType, but found none" ) + if model_type != 'SubmodelElementCollection': + raise DeserializationException( + f"Invalid modelType, expected 'SubmodelElementCollection', " + f"but got: {model_type!r}" + ) + for key, jsonable_value in jsonable.items(): setter_method = ( _SETTER_MAP_FOR_SUBMODEL_ELEMENT_COLLECTION.get(key) @@ -3988,11 +4016,18 @@ def property_from_jsonable( setter = _SetterForProperty() - if 'modelType' not in jsonable: + model_type = jsonable.get("modelType", None) + if model_type is None: raise DeserializationException( "Expected the property modelType, but found none" ) + if model_type != 'Property': + raise DeserializationException( + f"Invalid modelType, expected 'Property', " + f"but got: {model_type!r}" + ) + for key, jsonable_value in jsonable.items(): setter_method = ( _SETTER_MAP_FOR_PROPERTY.get(key) @@ -4378,11 +4413,18 @@ def multi_language_property_from_jsonable( setter = _SetterForMultiLanguageProperty() - if 'modelType' not in jsonable: + model_type = jsonable.get("modelType", None) + if model_type is None: raise DeserializationException( "Expected the property modelType, but found none" ) + if model_type != 'MultiLanguageProperty': + raise DeserializationException( + f"Invalid modelType, expected 'MultiLanguageProperty', " + f"but got: {model_type!r}" + ) + for key, jsonable_value in jsonable.items(): setter_method = ( _SETTER_MAP_FOR_MULTI_LANGUAGE_PROPERTY.get(key) @@ -4753,11 +4795,18 @@ def range_from_jsonable( setter = _SetterForRange() - if 'modelType' not in jsonable: + model_type = jsonable.get("modelType", None) + if model_type is None: raise DeserializationException( "Expected the property modelType, but found none" ) + if model_type != 'Range': + raise DeserializationException( + f"Invalid modelType, expected 'Range', " + f"but got: {model_type!r}" + ) + for key, jsonable_value in jsonable.items(): setter_method = ( _SETTER_MAP_FOR_RANGE.get(key) @@ -5106,11 +5155,18 @@ def reference_element_from_jsonable( setter = _SetterForReferenceElement() - if 'modelType' not in jsonable: + model_type = jsonable.get("modelType", None) + if model_type is None: raise DeserializationException( "Expected the property modelType, but found none" ) + if model_type != 'ReferenceElement': + raise DeserializationException( + f"Invalid modelType, expected 'ReferenceElement', " + f"but got: {model_type!r}" + ) + for key, jsonable_value in jsonable.items(): setter_method = ( _SETTER_MAP_FOR_REFERENCE_ELEMENT.get(key) @@ -5466,11 +5522,18 @@ def blob_from_jsonable( setter = _SetterForBlob() - if 'modelType' not in jsonable: + model_type = jsonable.get("modelType", None) + if model_type is None: raise DeserializationException( "Expected the property modelType, but found none" ) + if model_type != 'Blob': + raise DeserializationException( + f"Invalid modelType, expected 'Blob', " + f"but got: {model_type!r}" + ) + for key, jsonable_value in jsonable.items(): setter_method = ( _SETTER_MAP_FOR_BLOB.get(key) @@ -5832,11 +5895,18 @@ def file_from_jsonable( setter = _SetterForFile() - if 'modelType' not in jsonable: + model_type = jsonable.get("modelType", None) + if model_type is None: raise DeserializationException( "Expected the property modelType, but found none" ) + if model_type != 'File': + raise DeserializationException( + f"Invalid modelType, expected 'File', " + f"but got: {model_type!r}" + ) + for key, jsonable_value in jsonable.items(): setter_method = ( _SETTER_MAP_FOR_FILE.get(key) @@ -6235,11 +6305,18 @@ def annotated_relationship_element_from_jsonable( setter = _SetterForAnnotatedRelationshipElement() - if 'modelType' not in jsonable: + model_type = jsonable.get("modelType", None) + if model_type is None: raise DeserializationException( "Expected the property modelType, but found none" ) + if model_type != 'AnnotatedRelationshipElement': + raise DeserializationException( + f"Invalid modelType, expected 'AnnotatedRelationshipElement', " + f"but got: {model_type!r}" + ) + for key, jsonable_value in jsonable.items(): setter_method = ( _SETTER_MAP_FOR_ANNOTATED_RELATIONSHIP_ELEMENT.get(key) @@ -6681,11 +6758,18 @@ def entity_from_jsonable( setter = _SetterForEntity() - if 'modelType' not in jsonable: + model_type = jsonable.get("modelType", None) + if model_type is None: raise DeserializationException( "Expected the property modelType, but found none" ) + if model_type != 'Entity': + raise DeserializationException( + f"Invalid modelType, expected 'Entity', " + f"but got: {model_type!r}" + ) + for key, jsonable_value in jsonable.items(): setter_method = ( _SETTER_MAP_FOR_ENTITY.get(key) @@ -7435,11 +7519,18 @@ def basic_event_element_from_jsonable( setter = _SetterForBasicEventElement() - if 'modelType' not in jsonable: + model_type = jsonable.get("modelType", None) + if model_type is None: raise DeserializationException( "Expected the property modelType, but found none" ) + if model_type != 'BasicEventElement': + raise DeserializationException( + f"Invalid modelType, expected 'BasicEventElement', " + f"but got: {model_type!r}" + ) + for key, jsonable_value in jsonable.items(): setter_method = ( _SETTER_MAP_FOR_BASIC_EVENT_ELEMENT.get(key) @@ -7900,11 +7991,18 @@ def operation_from_jsonable( setter = _SetterForOperation() - if 'modelType' not in jsonable: + model_type = jsonable.get("modelType", None) + if model_type is None: raise DeserializationException( "Expected the property modelType, but found none" ) + if model_type != 'Operation': + raise DeserializationException( + f"Invalid modelType, expected 'Operation', " + f"but got: {model_type!r}" + ) + for key, jsonable_value in jsonable.items(): setter_method = ( _SETTER_MAP_FOR_OPERATION.get(key) @@ -8307,11 +8405,18 @@ def capability_from_jsonable( setter = _SetterForCapability() - if 'modelType' not in jsonable: + model_type = jsonable.get("modelType", None) + if model_type is None: raise DeserializationException( "Expected the property modelType, but found none" ) + if model_type != 'Capability': + raise DeserializationException( + f"Invalid modelType, expected 'Capability', " + f"but got: {model_type!r}" + ) + for key, jsonable_value in jsonable.items(): setter_method = ( _SETTER_MAP_FOR_CAPABILITY.get(key) @@ -8615,11 +8720,18 @@ def concept_description_from_jsonable( setter = _SetterForConceptDescription() - if 'modelType' not in jsonable: + model_type = jsonable.get("modelType", None) + if model_type is None: raise DeserializationException( "Expected the property modelType, but found none" ) + if model_type != 'ConceptDescription': + raise DeserializationException( + f"Invalid modelType, expected 'ConceptDescription', " + f"but got: {model_type!r}" + ) + for key, jsonable_value in jsonable.items(): setter_method = ( _SETTER_MAP_FOR_CONCEPT_DESCRIPTION.get(key) @@ -10366,11 +10478,18 @@ def data_specification_iec_61360_from_jsonable( setter = _SetterForDataSpecificationIEC61360() - if 'modelType' not in jsonable: + model_type = jsonable.get("modelType", None) + if model_type is None: raise DeserializationException( "Expected the property modelType, but found none" ) + if model_type != 'DataSpecificationIec61360': + raise DeserializationException( + f"Invalid modelType, expected 'DataSpecificationIec61360', " + f"but got: {model_type!r}" + ) + for key, jsonable_value in jsonable.items(): setter_method = ( _SETTER_MAP_FOR_DATA_SPECIFICATION_IEC_61360.get(key)