Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions sdk/basyx/aas/adapter/json/json_deserialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -635,8 +635,8 @@ def _construct_operation(cls, dct: Dict[str, object], object_class=model.Operati
def _construct_relationship_element(
cls, dct: Dict[str, object], object_class=model.RelationshipElement) -> model.RelationshipElement:
ret = object_class(id_short=None,
first=cls._construct_reference(_get_ts(dct, 'first', dict)),
second=cls._construct_reference(_get_ts(dct, 'second', dict)))
first=cls._construct_reference(_get_ts(dct, 'first', dict)) if 'first' in dct else None,
second=cls._construct_reference(_get_ts(dct, 'second', dict)) if 'second' in dct else None)
cls._amend_abstract_attributes(ret, dct)
return ret

Expand All @@ -646,8 +646,8 @@ def _construct_annotated_relationship_element(
-> model.AnnotatedRelationshipElement:
ret = object_class(
id_short=None,
first=cls._construct_reference(_get_ts(dct, 'first', dict)),
second=cls._construct_reference(_get_ts(dct, 'second', dict)))
first=cls._construct_reference(_get_ts(dct, 'first', dict)) if 'first' in dct else None,
second=cls._construct_reference(_get_ts(dct, 'second', dict)) if 'second' in dct else None)
cls._amend_abstract_attributes(ret, dct)
if not cls.stripped and 'annotations' in dct:
for element in _get_ts(dct, 'annotations', list):
Expand Down
5 changes: 4 additions & 1 deletion sdk/basyx/aas/adapter/json/json_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,10 @@ def _relationship_element_to_json(cls, obj: model.RelationshipElement) -> Dict[s
:return: dict with the serialized attributes of this object
"""
data = cls._abstract_classes_to_json(obj)
data.update({'first': obj.first, 'second': obj.second})
if obj.first is not None:
data.update({'first': obj.first})
if obj.second is not None:
data.update({'second': obj.second})
return data

@classmethod
Expand Down
4 changes: 2 additions & 2 deletions sdk/basyx/aas/adapter/xml/xml_deserialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -512,8 +512,8 @@ def _construct_relationship_element_internal(cls, element: etree._Element, objec
"""
relationship_element = object_class(
None,
_child_construct_mandatory(element, NS_AAS + "first", cls.construct_reference),
_child_construct_mandatory(element, NS_AAS + "second", cls.construct_reference)
_failsafe_construct(element.find(NS_AAS + "first"), cls.construct_reference, cls.failsafe),
_failsafe_construct(element.find(NS_AAS + "second"), cls.construct_reference, cls.failsafe)
)
cls._amend_abstract_attributes(relationship_element, element)
return relationship_element
Expand Down
4 changes: 2 additions & 2 deletions sdk/basyx/aas/model/submodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -904,8 +904,8 @@ class AnnotatedRelationshipElement(RelationshipElement, base.UniqueIdShortNamesp

def __init__(self,
id_short: Optional[base.NameType],
first: base.Reference,
second: base.Reference,
first: Optional[base.Reference] = None,
second: Optional[base.Reference] = None,
display_name: Optional[base.MultiLanguageNameType] = None,
annotation: Iterable[DataElement] = (),
category: Optional[base.NameType] = None,
Expand Down
25 changes: 25 additions & 0 deletions sdk/test/adapter/json/test_json_deserialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,31 @@ def test_stripped_annotated_relationship_element(self) -> None:
assert isinstance(are, model.AnnotatedRelationshipElement)
self.assertEqual(len(are.annotation), 0)

def test_optional_first_second_relationship_element(self) -> None:
data = """
{
"modelType": "RelationshipElement",
"idShort": "test_optional_second_relationship_element",
"category": "PARAMETER",
"first": {
"type": "ModelReference",
"keys": [
{
"type": "Submodel",
"value": "http://example.org/Test_Submodel"
},
{
"type": "AnnotatedRelationshipElement",
"value": "test_ref"
}
]
}
}"""

re = json.loads(data, cls=StrictAASFromJsonDecoder)
self.assertIsInstance(re, model.RelationshipElement)
self.assertIsNone(re.second)

def test_stripped_entity(self) -> None:
data = """
{
Expand Down
6 changes: 6 additions & 0 deletions sdk/test/adapter/json/test_json_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,12 @@ def test_stripped_annotated_relationship_element(self) -> None:

self._checkNormalAndStripped("annotations", are)

def test_relationship_element_omits_none_first_second(self) -> None:
re = model.RelationshipElement("test_re")
data = json.loads(json.dumps(re, cls=AASToJsonEncoder))
self.assertNotIn("first", data)
self.assertNotIn("second", data)

def test_stripped_entity(self) -> None:
mlp = model.MultiLanguageProperty("test_multi_language_property", category="PARAMETER")
entity = model.Entity("test_entity", model.EntityType.CO_MANAGED_ENTITY, statement=[mlp])
Expand Down
35 changes: 35 additions & 0 deletions sdk/test/adapter/xml/test_xml_deserialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,41 @@ def test_data_spec_iec61360_value_without_value_format(self) -> None:
self.assertEqual("test_value", ds_content.value)
self.assertIsNone(ds_content.value_format)

def test_optional_first_second_relationship_element(self) -> None:
xml = _xml_wrap("""
<aas:submodels>
<aas:submodel>
<aas:id>http://example.org/test_submodel</aas:id>
<aas:submodelElements>
<aas:relationshipElement>
<aas:idShort>test_optional_second_relationship_element</aas:idShort>
<aas:category>PARAMETER</aas:category>
<aas:first>
<aas:type>ModelReference</aas:type>
<aas:keys>
<aas:key>
<aas:type>Submodel</aas:type>
<aas:value>http://example.org/Test_Submodel</aas:value>
</aas:key>
<aas:key>
<aas:type>AnnotatedRelationshipElement</aas:type>
<aas:value>test_ref</aas:value>
</aas:key>
</aas:keys>
</aas:first>
</aas:relationshipElement>
</aas:submodelElements>
</aas:submodel>
</aas:submodels>
""")
object_store = read_aas_xml_file(io.StringIO(xml), failsafe=False)
submodel = object_store.get_item("http://example.org/test_submodel")
assert isinstance(submodel, model.Submodel)
re = submodel.get_referable("test_optional_second_relationship_element")
self.assertIsInstance(re, model.RelationshipElement)
assert isinstance(re, model.RelationshipElement)
self.assertIsNone(re.second)


class XmlDeserializationDerivingTest(unittest.TestCase):
def test_submodel_constructor_overriding(self) -> None:
Expand Down
Loading