Skip to content

sdk/server: Implement missing serialization modifier - #660

Draft
hpoeche wants to merge 13 commits into
eclipse-basyx:developfrom
rwth-iat:feat/serialization-formats
Draft

hpoeche wants to merge 13 commits into
eclipse-basyx:developfrom
rwth-iat:feat/serialization-formats

Conversation

@hpoeche

@hpoeche hpoeche commented Oct 10, 2026

Copy link
Copy Markdown
Contributor

This is WIP to implement and correct the missing serialization formats of the JSON serialization (defined in Part 1) as well as the modifiers that the server needs to offer (defined in Part 2).

  • Write down requirements of the serialization modifiers (which endpoint supports which combinations?) (Joshua)
  • PoC for serialization level modifier (core, deep) via pre-serializer processing (Joshua)
  • Implement metadata Serialization as subclass of AASToJsonEncoder (Henri)
  • Implement Reference Serialization as Subclass of AASToJsonEncoder (Henri)
  • Integrate Order-deterministic serialization (existing PR adapter.json: Add opt-in deterministic JSON array order #621)
  • Integrate all implemented serialization formats into the server

thammel and others added 5 commits August 31, 2026 17:58
All `$value` routes previously responded with `501 Not Implemented`.

The specification leaves some points open, which are resolved as
follows:

- Property values are mapped to the JSON type of their `valueType`, so
  booleans and numbers are not quoted. Values without a JSON literal
  are returned as their XSD representation.
  `xs:integer` is unbounded in both Python and JSON and is
  therefore passed on as it is.
- Elements without a value are omitted from Submodels, collections and
  Entity statements, but kept as `null` within a SubmodelElementList, as
  omitting them would shift the indices of the remaining elements.
- Operations and Capabilities have no ValueOnly representation. They are
  skipped in containers and result in `400 Bad Request` when requested
  directly, which is how the `$metadata` route handles them.
- `level=core` returns the direct children of the requested resource and
  their nested containers as empty, matching the examples of the
  specification.
- The entries of `GET /submodels/{id}/submodel-elements/$value` are
  mapped by idShort, as a bare `SubmodelElementValue` would lose the
  association between a value and its element. Submodels are returned
  as-is, since their entries are already named.
- The languages of a MultiLanguageProperty are sorted, to keep the order
  of the response deterministic.

The ValueOnly serialization is only defined for JSON, so the routes
respond with `406 Not Acceptable` if XML is requested. `extent` remains
unimplemented, as on all other routes. `PATCH …/$value`, the `$path`
routes and the operation invocation routes are not part of this change.

Fixes eclipse-basyx#446
Previously, the ValueOnly serialization was method based in the
server package. These changes shift the code from serve package to
the sdk, as ValueOnly serialiaztion is defined in Part 1 of the spec.
Additionally, I restructured the code to live inside a `JSONEncoder`
subclass, so it can be used the same way as `AASToJsonEncoder`.

Still TODO:
- Implement `level=core` serialization
- Integrate this serialiazer into the server
Showcases collapse of rules and handler methods using converter and modifiers

@hpoeche hpoeche left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like the idea of the ContentModifierConverter for the endpoint definition! I found some small things we should discuss, so I can mirror them for the ValueOnly encoder.

Comment thread server/app/adapter/modifiers.py Outdated
Comment thread server/app/adapter/modifiers.py Outdated
Comment thread sdk/basyx/aas/adapter/json/path_serialization.py Outdated
Comment thread sdk/basyx/aas/adapter/json/path_serialization.py Outdated
Previously, `PathJsonEncoder` carried `deep` as a class variable
  and the `?level=core` variant was expressed by a dedicated
  `ShallowPathJsonEncoder` subclass that flipped it to `False`.
  Composing this axis with other content modes required another
  `Shallow*` sibling per encoder, so the class count scaled with
  the Cartesian product of axes.

  With this change, `deep` is passed to `PathJsonEncoder.__init__`
  as a keyword argument and stored on the instance. The encoder
  uses `default()` directly instead of extending the classmethod-
  based `_get_aas_class_serializers` dispatch, which avoids
  `cls`/`self` mismatches along the MRO when carrying per-request
  state. On the server side, `pick_encoder` now returns
  `(cls, kwargs)` and `JsonResponse.serialize` forwards the kwargs
  through `json.dumps` into the encoder constructor;
  `ResultShallowPathJsonEncoder` is removed since the single
  `ResultPathJsonEncoder` now covers both levels via the kwarg.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants