Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
4413c62
server: Implement the read-only `$value` routes
thammel Aug 31, 2026
f9f9f88
Merge branch 'feat/446-get-value-endpoints' into feat/serialization-f…
hpoeche Oct 9, 2026
a1480a0
Integrate /$value endpoint tests with existing tests
hpoeche Oct 9, 2026
bff5dc7
Rework ValueOnly serialization as `JSONEncoder` subclass
hpoeche Oct 10, 2026
00016d9
Rewire path serialization into JSON Encoder subclass
JAB1305 Oct 10, 2026
2aec044
Draft central modifier and encoder handling
JAB1305 Oct 11, 2026
54a49f4
Exmplatory wire up for get submodel endpoint
JAB1305 Oct 11, 2026
fde20f9
Implement Reference-Serialization as AASToJsonEnocoder subclass
hpoeche Oct 14, 2026
39d4685
Implement Metadata-Serialization as AASToJsonEnocoder subclass
hpoeche Oct 14, 2026
bd0dbcd
Fix import errors and missing copyright
hpoeche Oct 14, 2026
2fe15bc
Add testing for path serialization
hpoeche Oct 14, 2026
ca6a363
Refactor bool related names for better clarity
JAB1305 Oct 14, 2026
1e45688
Adjust `deep` as init arg on path encoder
JAB1305 Oct 19, 2026
d6dbf01
Refine `PathJsonEncoder` testing
hpoeche Oct 23, 2026
c04a436
Implement modifier `level=core` for Normal and ValueOnly
hpoeche Oct 25, 2026
1d10056
Refactor adapter structure
JAB1305 Oct 27, 2026
08fc93b
Restructure imports
JAB1305 Oct 27, 2026
c57c2cb
Restrurcture tests following serializers
JAB1305 Oct 27, 2026
b92fd60
Create metadata serialization test
JAB1305 Oct 27, 2026
05f8eb4
Implement modifier `extent=WithoutBLOBValue`
hpoeche Oct 27, 2026
7321c0c
Realign path serialization and tests with supported types
JAB1305 Oct 27, 2026
93b98cf
Merge branch 'feat/serialization-formats' of https://github.com/rwth-…
JAB1305 Oct 27, 2026
873c00b
Fail Metadata serialization on unsupported types
hpoeche Oct 27, 2026
4bb8e0f
Fix docstrings
hpoeche Oct 27, 2026
2ea9b55
Implement constant order in JSON serialization
hpoeche Oct 28, 2026
3c43a08
Make `modelType` first key in serialization of `model.Referable`s
hpoeche Oct 29, 2026
37e01c2
Restructure existing serialization related tests
JAB1305 Nov 2, 2026
61a3c8a
Draft $path endpoint tests
JAB1305 Nov 2, 2026
d32613c
Draft $metadata endpoints
JAB1305 Nov 5, 2026
44a7830
Draft $value endpoint tests
JAB1305 Nov 6, 2026
4c22604
Integrate serialization formats into submodel routes
hpoeche Nov 8, 2026
97ac37e
`../submodel-elements/$path`: Paginate over list of paths
hpoeche Nov 8, 2026
21141c2
Apply to all `/$` routes + fix XML reference
hpoeche Nov 8, 2026
6adcce2
Fix XML serialization and GET `/shells/$reference`
hpoeche Nov 9, 2026
c02a83c
Implement `/$reference` tests for all routes
hpoeche Nov 9, 2026
6d9c099
Finalize `/$path` tests
hpoeche Nov 12, 2026
3f01a9a
Make `/$path` return flat list
hpoeche Nov 12, 2026
7d6f4c0
Fix serialization of idShort of Elements in SML
hpoeche Nov 12, 2026
08172d2
Omit `Operation` and `Capability` for `/$metadata`
hpoeche Nov 12, 2026
ec4c12d
Reject limit < 1 and $path on elements without idShort
hpoeche Nov 12, 2026
f677518
Test extent and level handling of Normal Submodel routes
hpoeche Nov 12, 2026
cb31ca9
Fix tests for `..submodel-elements/$value`
hpoeche Nov 12, 2026
f997641
Add docstrings and comments
hpoeche Nov 12, 2026
2ac676f
Update server `README.md`
hpoeche Nov 12, 2026
831452e
Remove unsupported `@overwrite` decorator
hpoeche Nov 12, 2026
4397bad
Reject `?level=core` on `/shells` and `/concept-description`
hpoeche Nov 12, 2026
df2fd84
Merge branch 'develop' into feat/serialization-formats
hpoeche Nov 12, 2026
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
22 changes: 21 additions & 1 deletion sdk/basyx/aas/adapter/_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,30 @@
"""

import os
from typing import IO, BinaryIO, Dict, Type, Union
from typing import IO, BinaryIO, Dict, Optional, Type, Union

from basyx.aas import model


def serializable_id_short(obj: model.Referable) -> Optional[str]:
"""
Get the idShort of a :class:`~basyx.aas.model.base.Referable` as it is to be serialized. This hides the internal
idShort (prefixed with :attr:`~basyx.aas.model.submodel.SubmodelElementList.GENERATED_ID_SHORT_PREFIX`), which
the SDK generates for elements of a :class:`~basyx.aas.model.submodel.SubmodelElementList` added without an
idShort. For these, ``None`` is returned, while all other idShorts are returned as they are.

:param obj: The Referable to get the idShort of
:return: The idShort to serialize or ``None``, if no idShort is to be serialized
"""
if (
obj.id_short is not None
and isinstance(obj.parent, model.SubmodelElementList)
and obj.id_short.startswith(model.SubmodelElementList.GENERATED_ID_SHORT_PREFIX)
):
return None
return obj.id_short


# type aliases for path-like objects and IO
# used by write_aas_xml_file, read_aas_xml_file, write_aas_json_file, read_aas_json_file
Path = Union[str, bytes, os.PathLike]
Expand Down
107 changes: 107 additions & 0 deletions sdk/basyx/aas/adapter/_ordering.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
#
# SPDX-License-Identifier: MIT
"""
Sort keys for the canonical order of collections that are unordered according to the specification (every
multi-valued attribute not marked ``<<ordered>>``).
"""
from typing import Optional, Tuple

from basyx.aas import model

from . import _generic


def reference_sort_key(obj: Optional[model.Reference]) -> Tuple:
"""
Sort key of a Reference, consisting of its type, its keys and its referredSemanticId

:param obj: object of class Reference or ``None``
:return: tuple which totally orders References, ``()`` for ``None``
"""
if obj is None:
return ()
return (
_generic.REFERENCE_TYPES[obj.__class__],
tuple((_generic.KEY_TYPES[key.type], key.value) for key in obj.key),
reference_sort_key(obj.referred_semantic_id),
)


def specific_asset_id_sort_key(obj: model.SpecificAssetId) -> Tuple:
"""
Sort key of a SpecificAssetId, consisting of all its attributes, since its name is not unique

:param obj: object of class SpecificAssetId
:return: tuple which totally orders SpecificAssetIds
"""
return (
obj.name,
obj.value,
reference_sort_key(obj.external_subject_id),
reference_sort_key(obj.semantic_id),
tuple(sorted(reference_sort_key(ref) for ref in obj.supplemental_semantic_id)),
)


def embedded_data_specification_sort_key(obj: model.EmbeddedDataSpecification) -> Tuple:
"""
Sort key of an EmbeddedDataSpecification, consisting of its dataSpecification

:param obj: object of class EmbeddedDataSpecification
:return: tuple which totally orders EmbeddedDataSpecifications
"""
return reference_sort_key(obj.data_specification)


def value_reference_pair_sort_key(obj: model.ValueReferencePair) -> Tuple:
"""
Sort key of a ValueReferencePair, consisting of its value and its valueId, since its value is not unique

:param obj: object of class ValueReferencePair
:return: tuple which totally orders ValueReferencePairs
"""
return model.datatypes.xsd_repr(obj.value), reference_sort_key(obj.value_id)


def extension_sort_key(obj: model.Extension) -> str:
"""
Sort key of an Extension, its name, which is unique (Constraint AASd-077)

:param obj: object of class Extension
:return: name of the Extension
"""
return obj.name


def qualifier_sort_key(obj: model.Qualifier) -> str:
"""
Sort key of a Qualifier, its type, which is unique (Constraint AASd-021)

:param obj: object of class Qualifier
:return: type of the Qualifier
"""
return obj.type


def referable_sort_key(obj: model.Referable) -> str:
"""
Sort key of a Referable within its namespace, its idShort, which is unique (Constraint AASd-022)

:param obj: object of class Referable
:return: idShort of the Referable, ``""`` if it has none
"""
return obj.id_short or ""


def identifiable_sort_key(obj: model.Identifiable) -> str:
"""
Sort key of an Identifiable, its globally unique id

:param obj: object of class Identifiable
:return: id of the Identifiable
"""
return obj.id
15 changes: 15 additions & 0 deletions sdk/basyx/aas/adapter/json/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,19 @@
object. A function :meth:`~basyx.aas.adapter.json.json_deserialization.read_aas_json_file` is provided to read all
AAS objects within a JSON file and return them as BaSyx Python SDK
:class:`ObjectStore <basyx.aas.model.provider.AbstractObjectStore>`.

``formats``: The subpackage offers JSONEncoders for the serialization formats of the AAS API specification (Part 2),
which are selected with a content modifier:
:class:`~basyx.aas.adapter.json.formats.metadata_serialization.MetadataJsonEncoder` (``$metadata``),
:class:`~basyx.aas.adapter.json.formats.value_only_serialization.ValueOnlyJsonEncoder` (``$value``),
:class:`~basyx.aas.adapter.json.formats.reference_serialization.ReferenceJsonEncoder` (``$reference``) and
:class:`~basyx.aas.adapter.json.formats.path_serialization.PathJsonEncoder` (``$path``).
"""

from .formats.metadata_serialization import MetadataJsonEncoder
from .formats.path_serialization import PathJsonEncoder
from .formats.reference_serialization import ReferenceJsonEncoder
from .formats.value_only_serialization import ValueOnlyJsonEncoder
from .json_deserialization import (
AASFromJsonDecoder,
StrictAASFromJsonDecoder,
Expand All @@ -43,4 +54,8 @@
"StrippedAASToJsonEncoder",
"object_store_to_json",
"write_aas_json_file",
"PathJsonEncoder",
"ValueOnlyJsonEncoder",
"MetadataJsonEncoder",
"ReferenceJsonEncoder",
]
149 changes: 149 additions & 0 deletions sdk/basyx/aas/adapter/json/formats/metadata_serialization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
#
# SPDX-License-Identifier: MIT
"""
Module for serializing Asset Administration Shell objects to the JSON format "Metadata" (content modifier
``$metadata``)
"""
from typing import Callable, Dict, Type

from basyx.aas import model

from ..json_serialization import AASToJsonEncoder


class MetadataJsonEncoder(AASToJsonEncoder):
"""
Custom JSONEncoder that serializes BaSyx Python SDK objects into the JSON format "Metadata". This equals
the normal serialization format but omits certain keys holding the values, leaving only the metadata of the object.

Objects without a Metadata serialization (AssetInformation, Resource, SpecificAssetId, ConceptDescription) raise
a :class:`TypeError`.
"""

@classmethod
def _get_aas_class_serializers(cls) -> Dict[Type, Callable]:
mapping = super()._get_aas_class_serializers()

# Without a serializer, the encoder raises a TypeError for these objects

mapping.pop(model.AssetInformation, None)
mapping.pop(model.Resource, None)
mapping.pop(model.SpecificAssetId, None)
mapping.pop(model.ConceptDescription, None)

return mapping

@classmethod
def _asset_administration_shell_to_json(
cls, obj: model.AssetAdministrationShell
) -> Dict[str, object]:
data = super()._asset_administration_shell_to_json(obj)
data.pop("assetInformation", None)
data.pop("submodels", None)
return data

@classmethod
def _submodel_to_json(
cls, obj: model.Submodel
) -> Dict[str, object]:
data = super()._submodel_to_json(obj)
data.pop("submodelElements", None)
return data

@classmethod
def _submodel_element_collection_to_json(
cls, obj: model.SubmodelElementCollection
) -> Dict[str, object]:
data = super()._submodel_element_collection_to_json(obj)
data.pop("value", None)
return data

@classmethod
def _submodel_element_list_to_json(
cls, obj: model.SubmodelElementList
) -> Dict[str, object]:
data = super()._submodel_element_list_to_json(obj)
data.pop("value", None)
return data

@classmethod
def _entity_to_json(cls, obj: model.Entity) -> Dict[str, object]:
data = super()._entity_to_json(obj)
data.pop("statements", None)
data.pop("globalAssetId", None)
data.pop("specificAssetIds", None)
return data

@classmethod
def _basic_event_element_to_json(
cls, obj: model.BasicEventElement
) -> Dict[str, object]:
data = super()._basic_event_element_to_json(obj)
data.pop("observed", None)
return data

@classmethod
def _property_to_json(cls, obj: model.Property) -> Dict[str, object]:
data = super()._property_to_json(obj)
data.pop("value", None)
data.pop("valueId", None)
return data

@classmethod
def _multi_language_property_to_json(cls, obj: model.MultiLanguageProperty) -> Dict[str, object]:
data = super()._multi_language_property_to_json(obj)
data.pop("value", None)
data.pop("valueId", None)
return data

@classmethod
def _range_to_json(cls, obj: model.Range) -> Dict[str, object]:
data = super()._range_to_json(obj)
data.pop("min", None)
data.pop("max", None)
return data

@classmethod
def _reference_element_to_json(
cls, obj: model.ReferenceElement
) -> Dict[str, object]:
data = super()._reference_element_to_json(obj)
data.pop("value", None)
return data

@classmethod
def _relationship_element_to_json(
cls, obj: model.RelationshipElement
) -> Dict[str, object]:
data = super()._relationship_element_to_json(obj)
data.pop("first", None)
data.pop("second", None)
return data

@classmethod
def _annotated_relationship_element_to_json(
cls, obj: model.AnnotatedRelationshipElement
) -> Dict[str, object]:
data = super()._annotated_relationship_element_to_json(obj)
data.pop("first", None)
data.pop("second", None)
data.pop("annotations", None)
return data

@classmethod
def _blob_to_json(cls, obj: model.Blob) -> Dict[str, object]:
data = super()._blob_to_json(obj)
data.pop("value", None)
data.pop("contentType", None)
return data

@classmethod
def _file_to_json(cls, obj: model.File) -> Dict[str, object]:
data = super()._file_to_json(obj)
data.pop("value", None)
data.pop("contentType", None)
return data
Loading
Loading