Skip to main content
Sign in
Snippets Groups Projects
Commit 06bc235b authored by Sebastian Heppner's avatar Sebastian Heppner
Browse files

Merge branch 'feature/xml_serialization' into 'master'

Feature/xml serialization merge

See merge request acplt/pyaas!11
parents c480e46d 772be7ad
No related branches found
No related tags found
No related merge requests found
Pipeline #249950 passed
"""
Dicts to serialize enum classes
"""
from typing import Dict
from aas import model
MODELING_KIND: Dict[model.ModelingKind, str] = {
model.ModelingKind.TEMPLATE: 'Template',
model.ModelingKind.INSTANCE: 'Instance'}
ASSET_KIND: Dict[model.AssetKind, str] = {
model.AssetKind.TYPE: 'Type',
model.AssetKind.INSTANCE: 'Instance'}
KEY_ELEMENTS: Dict[model.KeyElements, str] = {
model.KeyElements.ASSET: 'Asset',
model.KeyElements.ASSET_ADMINISTRATION_SHELL: 'AssetAdministrationShell',
model.KeyElements.CONCEPT_DESCRIPTION: 'ConceptDescription',
model.KeyElements.SUBMODEL: 'Submodel',
model.KeyElements.ANNOTATED_RELATIONSHIP_ELEMENT: 'AnnotatedRelationshipElement',
model.KeyElements.BASIC_EVENT: 'BasicEvent',
model.KeyElements.BLOB: 'Blob',
model.KeyElements.CAPABILITY: 'Capability',
model.KeyElements.CONCEPT_DICTIONARY: 'ConceptDictionary',
model.KeyElements.DATA_ELEMENT: 'DataElement',
model.KeyElements.ENTITY: 'Entity',
model.KeyElements.EVENT: 'Event',
model.KeyElements.FILE: 'File',
model.KeyElements.MULTI_LANGUAGE_PROPERTY: 'MultiLanguageProperty',
model.KeyElements.OPERATION: 'Operation',
model.KeyElements.PROPERTY: 'Property',
model.KeyElements.RANGE: 'Range',
model.KeyElements.REFERENCE_ELEMENT: 'ReferenceElement',
model.KeyElements.RELATIONSHIP_ELEMENT: 'RelationshipElement',
model.KeyElements.SUBMODEL_ELEMENT: 'SubmodelElement',
model.KeyElements.SUBMODEL_ELEMENT_COLLECTION: 'SubmodelElementCollection',
model.KeyElements.VIEW: 'View',
model.KeyElements.GLOBAL_REFERENCE: 'GlobalReference',
model.KeyElements.FRAGMENT_REFERENCE: 'FragmentReference'}
KEY_TYPES: Dict[model.KeyType, str] = {
model.KeyType.CUSTOM: 'Custom',
model.KeyType.IRDI: 'IRDI',
model.KeyType.IRI: 'IRI',
model.KeyType.IDSHORT: 'IdShort',
model.KeyType.FRAGMENT_ID: 'FragmentId'}
IDENTIFIER_TYPES: Dict[model.IdentifierType, str] = {
model.IdentifierType.CUSTOM: 'Custom',
model.IdentifierType.IRDI: 'IRDI',
model.IdentifierType.IRI: 'IRI'}
ENTITY_TYPES: Dict[model.EntityType, str] = {
model.EntityType.CO_MANAGED_ENTITY: 'CoManagedEntity',
model.EntityType.SELF_MANAGED_ENTITY: 'SelfManagedEntity'}
IEC61360_DATA_TYPES: Dict[model.concept.IEC61360DataType, str] = {
model.concept.IEC61360DataType.DATE: 'DATE',
model.concept.IEC61360DataType.STRING: 'STRING',
model.concept.IEC61360DataType.STRING_TRANSLATABLE: 'STRING_TRANSLATABLE',
model.concept.IEC61360DataType.REAL_MEASURE: 'REAL_MEASURE',
model.concept.IEC61360DataType.REAL_COUNT: 'REAL_COUNT',
model.concept.IEC61360DataType.REAL_CURRENCY: 'REAL_CURRENCY',
model.concept.IEC61360DataType.BOOLEAN: 'BOOLEAN',
model.concept.IEC61360DataType.URL: 'URL',
model.concept.IEC61360DataType.RATIONAL: 'RATIONAL',
model.concept.IEC61360DataType.RATIONAL_MEASURE: 'RATIONAL_MEASURE',
model.concept.IEC61360DataType.TIME: 'TIME',
model.concept.IEC61360DataType.TIMESTAMP: 'TIMESTAMP',
}
IEC61360_LEVEL_TYPES: Dict[model.concept.IEC61360LevelType, str] = {
model.concept.IEC61360LevelType.MIN: 'Min',
model.concept.IEC61360LevelType.MAX: 'Max',
model.concept.IEC61360LevelType.NOM: 'Nom',
model.concept.IEC61360LevelType.TYP: 'Typ',
}
......@@ -29,7 +29,7 @@ import pprint
from typing import Dict, Callable, TypeVar, Type, List, IO, Optional
from ... import model
from .json_serialization import MODELING_KIND, ASSET_KIND, KEY_ELEMENTS, KEY_TYPES, IDENTIFIER_TYPES, ENTITY_TYPES,\
from .._generic import MODELING_KIND, ASSET_KIND, KEY_ELEMENTS, KEY_TYPES, IDENTIFIER_TYPES, ENTITY_TYPES,\
IEC61360_DATA_TYPES, IEC61360_LEVEL_TYPES
logger = logging.getLogger(__name__)
......
......
......@@ -29,83 +29,7 @@ from typing import List, Dict, IO
import json
from ... import model
# ##########################################
# dicts to serialize enum values to json
# ##########################################
MODELING_KIND: Dict[model.ModelingKind, str] = {
model.ModelingKind.TEMPLATE: 'Template',
model.ModelingKind.INSTANCE: 'Instance'}
ASSET_KIND: Dict[model.AssetKind, str] = {
model.AssetKind.TYPE: 'Type',
model.AssetKind.INSTANCE: 'Instance'}
KEY_ELEMENTS: Dict[model.KeyElements, str] = {
model.KeyElements.ASSET: 'Asset',
model.KeyElements.ASSET_ADMINISTRATION_SHELL: 'AssetAdministrationShell',
model.KeyElements.CONCEPT_DESCRIPTION: 'ConceptDescription',
model.KeyElements.SUBMODEL: 'Submodel',
model.KeyElements.ANNOTATED_RELATIONSHIP_ELEMENT: 'AnnotatedRelationshipElement',
model.KeyElements.BASIC_EVENT: 'BasicEvent',
model.KeyElements.BLOB: 'Blob',
model.KeyElements.CAPABILITY: 'Capability',
model.KeyElements.CONCEPT_DICTIONARY: 'ConceptDictionary',
model.KeyElements.ENTITY: 'Entity',
model.KeyElements.EVENT: 'Event',
model.KeyElements.FILE: 'File',
model.KeyElements.MULTI_LANGUAGE_PROPERTY: 'MultiLanguageProperty',
model.KeyElements.OPERATION: 'Operation',
model.KeyElements.PROPERTY: 'Property',
model.KeyElements.RANGE: 'Range',
model.KeyElements.REFERENCE_ELEMENT: 'ReferenceElement',
model.KeyElements.RELATIONSHIP_ELEMENT: 'RelationshipElement',
model.KeyElements.SUBMODEL_ELEMENT: 'SubmodelElement',
model.KeyElements.SUBMODEL_ELEMENT_COLLECTION: 'SubmodelElementCollection',
model.KeyElements.VIEW: 'View',
model.KeyElements.GLOBAL_REFERENCE: 'GlobalReference',
model.KeyElements.FRAGMENT_REFERENCE: 'FragmentReference',
model.KeyElements.DATA_ELEMENT: 'DataElement'}
KEY_TYPES: Dict[model.KeyType, str] = {
model.KeyType.CUSTOM: 'Custom',
model.KeyType.IRDI: 'IRDI',
model.KeyType.IRI: 'IRI',
model.KeyType.IDSHORT: 'IdShort',
model.KeyType.FRAGMENT_ID: 'FragmentId'}
IDENTIFIER_TYPES: Dict[model.IdentifierType, str] = {
model.IdentifierType.CUSTOM: 'Custom',
model.IdentifierType.IRDI: 'IRDI',
model.IdentifierType.IRI: 'IRI'}
ENTITY_TYPES: Dict[model.EntityType, str] = {
model.EntityType.CO_MANAGED_ENTITY: 'CoManagedEntity',
model.EntityType.SELF_MANAGED_ENTITY: 'SelfManagedEntity'}
IEC61360_DATA_TYPES: Dict[model.concept.IEC61360DataType, str] = {
model.concept.IEC61360DataType.DATE: 'DATE',
model.concept.IEC61360DataType.STRING: 'STRING',
model.concept.IEC61360DataType.STRING_TRANSLATABLE: 'STRING_TRANSLATABLE',
model.concept.IEC61360DataType.REAL_MEASURE: 'REAL_MEASURE',
model.concept.IEC61360DataType.REAL_COUNT: 'REAL_COUNT',
model.concept.IEC61360DataType.REAL_CURRENCY: 'REAL_CURRENCY',
model.concept.IEC61360DataType.BOOLEAN: 'BOOLEAN',
model.concept.IEC61360DataType.URL: 'URL',
model.concept.IEC61360DataType.RATIONAL: 'RATIONAL',
model.concept.IEC61360DataType.RATIONAL_MEASURE: 'RATIONAL_MEASURE',
model.concept.IEC61360DataType.TIME: 'TIME',
model.concept.IEC61360DataType.TIMESTAMP: 'TIMESTAMP',
}
IEC61360_LEVEL_TYPES: Dict[model.concept.IEC61360LevelType, str] = {
model.concept.IEC61360LevelType.MIN: 'Min',
model.concept.IEC61360LevelType.MAX: 'Max',
model.concept.IEC61360LevelType.NOM: 'Nom',
model.concept.IEC61360LevelType.TYP: 'Typ',
}
from .. import _generic
def abstract_classes_to_json(obj: object) -> Dict[str, object]:
......@@ -137,7 +61,7 @@ def abstract_classes_to_json(obj: object) -> Dict[str, object]:
data['semanticId'] = obj.semantic_id
if isinstance(obj, model.HasKind):
if obj.kind is model.ModelingKind.TEMPLATE:
data['kind'] = MODELING_KIND[obj.kind]
data['kind'] = _generic.MODELING_KIND[obj.kind]
if isinstance(obj, model.Qualifiable):
if obj.qualifier:
data['qualifiers'] = list(obj.qualifier)
......@@ -161,8 +85,8 @@ def key_to_json(obj: model.Key) -> Dict[str, object]:
:return: dict with the serialized attributes of this object
"""
data = abstract_classes_to_json(obj)
data.update({'type': KEY_ELEMENTS[obj.type],
'idType': KEY_TYPES[obj.id_type],
data.update({'type': _generic.KEY_ELEMENTS[obj.type],
'idType': _generic.KEY_TYPES[obj.id_type],
'value': obj.value,
'local': obj.local})
return data
......@@ -192,7 +116,7 @@ def identifier_to_json(obj: model.Identifier) -> Dict[str, object]:
"""
data = abstract_classes_to_json(obj)
data['id'] = obj.id
data['idType'] = IDENTIFIER_TYPES[obj.id_type]
data['idType'] = _generic.IDENTIFIER_TYPES[obj.id_type]
return data
......@@ -316,7 +240,7 @@ def asset_to_json(obj: model.Asset) -> Dict[str, object]:
:return: dict with the serialized attributes of this object
"""
data = abstract_classes_to_json(obj)
data['kind'] = ASSET_KIND[obj.kind]
data['kind'] = _generic.ASSET_KIND[obj.kind]
if obj.asset_identification_model:
data['assetIdentificationModel'] = obj.asset_identification_model
if obj.bill_of_material:
......@@ -353,7 +277,7 @@ def append_iec61360_concept_description_attrs(obj: model.concept.IEC61360Concept
"""
data_spec = {
'preferredName': lang_string_set_to_json(obj.preferred_name),
'dataType': IEC61360_DATA_TYPES[obj.data_type],
'dataType': _generic.IEC61360_DATA_TYPES[obj.data_type],
}
if obj.definition is not None:
data_spec['definition'] = lang_string_set_to_json(obj.definition)
......@@ -376,7 +300,7 @@ def append_iec61360_concept_description_attrs(obj: model.concept.IEC61360Concept
if obj.value_id is not None:
data_spec['valueId'] = obj.value_id
if obj.level_types:
data_spec['levelType'] = [IEC61360_LEVEL_TYPES[lt] for lt in obj.level_types]
data_spec['levelType'] = [_generic.IEC61360_LEVEL_TYPES[lt] for lt in obj.level_types]
data['embeddedDataSpecifications'] = [
{'dataSpecification': model.Reference((
model.Key(model.KeyElements.GLOBAL_REFERENCE, False,
......@@ -639,7 +563,7 @@ def entity_to_json(obj: model.Entity) -> Dict[str, object]:
data = abstract_classes_to_json(obj)
if obj.statement:
data['statements'] = list(obj.statement)
data['entityType'] = ENTITY_TYPES[obj.entity_type]
data['entityType'] = _generic.ENTITY_TYPES[obj.entity_type]
if obj.asset:
data['asset'] = obj.asset
return data
......
......
# Copyright 2019 PyI40AAS Contributors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
"""
Module for deserializing Asset Administration Shell data from the official XML format
"""
This diff is collapsed.
jsonschema>=3.2,<4.0
lxml>=4.2,<5
python-dateutil>=2.8,<3.0
\ No newline at end of file
# XML schema should not be added to the Git repository due to license concerns
AAS.xsd
AAS_ABAC.xsd
IEC61360.xsd
\ No newline at end of file
# Copyright 2019 PyI40AAS Contributors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
import io
import unittest
from lxml import etree # type: ignore
import os
from aas import model
from aas.adapter.xml import xml_serialization
from aas.examples.data import example_aas_missing_attributes, example_submodel_template, \
example_aas_mandatory_attributes, example_aas
XML_SCHEMA_FILE = os.path.join(os.path.dirname(__file__), 'AAS.xsd')
class XMLSerializationTest(unittest.TestCase):
def test_serialize_object(self) -> None:
test_object = model.Property("test_id_short",
model.datatypes.String,
category="PARAMETER",
description={"en-us": "Germany", "de": "Deutschland"})
xml_data = xml_serialization.property_to_xml(test_object, xml_serialization.NS_AAS, "test_object")
# todo: is this a correct way to test it?
def test_random_object_serialization(self) -> None:
asset_key = (model.Key(model.KeyElements.ASSET, True, "asset", model.KeyType.CUSTOM),)
asset_reference = model.AASReference(asset_key, model.Asset)
aas_identifier = model.Identifier("AAS1", model.IdentifierType.CUSTOM)
submodel_key = (model.Key(model.KeyElements.SUBMODEL, True, "SM1", model.KeyType.CUSTOM),)
submodel_identifier = submodel_key[0].get_identifier()
assert (submodel_identifier is not None)
submodel_reference = model.AASReference(submodel_key, model.Submodel)
submodel = model.Submodel(submodel_identifier)
test_aas = model.AssetAdministrationShell(asset_reference, aas_identifier, submodel_={submodel_reference})
test_data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
test_data.add(test_aas)
test_data.add(submodel)
test_file = io.BytesIO()
xml_serialization.write_aas_xml_file(file=test_file, data=test_data)
@unittest.skipUnless(os.path.exists(XML_SCHEMA_FILE), "XML Schema not found for validation")
class XMLSerializationSchemaTest(unittest.TestCase):
def test_random_object_serialization(self) -> None:
asset_key = (model.Key(model.KeyElements.ASSET, True, "asset", model.KeyType.CUSTOM),)
asset_reference = model.AASReference(asset_key, model.Asset)
aas_identifier = model.Identifier("AAS1", model.IdentifierType.CUSTOM)
submodel_key = (model.Key(model.KeyElements.SUBMODEL, True, "SM1", model.KeyType.CUSTOM),)
submodel_identifier = submodel_key[0].get_identifier()
assert(submodel_identifier is not None)
submodel_reference = model.AASReference(submodel_key, model.Submodel)
submodel = model.Submodel(submodel_identifier, semantic_id=model.Reference((),))
test_aas = model.AssetAdministrationShell(asset_reference, aas_identifier, submodel_={submodel_reference})
# serialize object to xml
test_data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
test_data.add(test_aas)
test_data.add(submodel)
test_file = io.BytesIO()
xml_serialization.write_aas_xml_file(file=test_file, data=test_data)
# load schema
aas_schema = etree.XMLSchema(file=XML_SCHEMA_FILE)
# validate serialization against schema
parser = etree.XMLParser(schema=aas_schema)
test_file.seek(0)
root = etree.parse(test_file, parser=parser)
def test_full_example_serialization(self) -> None:
data = example_aas.create_full_example()
file = io.BytesIO()
xml_serialization.write_aas_xml_file(file=file, data=data)
# load schema
aas_schema = etree.XMLSchema(file=XML_SCHEMA_FILE)
# validate serialization against schema
parser = etree.XMLParser(schema=aas_schema)
file.seek(0)
root = etree.parse(file, parser=parser)
def test_submodel_template_serialization(self) -> None:
data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
data.add(example_submodel_template.create_example_submodel_template())
file = io.BytesIO()
xml_serialization.write_aas_xml_file(file=file, data=data)
# load schema
aas_schema = etree.XMLSchema(file=XML_SCHEMA_FILE)
# validate serialization against schema
parser = etree.XMLParser(schema=aas_schema)
file.seek(0)
root = etree.parse(file, parser=parser)
def test_full_empty_example_serialization(self) -> None:
data = example_aas_mandatory_attributes.create_full_example()
file = io.BytesIO()
xml_serialization.write_aas_xml_file(file=file, data=data)
# load schema
aas_schema = etree.XMLSchema(file=XML_SCHEMA_FILE)
# validate serialization against schema
parser = etree.XMLParser(schema=aas_schema)
file.seek(0)
root = etree.parse(file, parser=parser)
def test_missing_serialization(self) -> None:
data = example_aas_missing_attributes.create_full_example()
file = io.BytesIO()
xml_serialization.write_aas_xml_file(file=file, data=data)
# load schema
aas_schema = etree.XMLSchema(file=XML_SCHEMA_FILE)
# validate serialization against schema
parser = etree.XMLParser(schema=aas_schema)
file.seek(0)
root = etree.parse(file, parser=parser)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment