JSON Labs

Syside supports bi-directional JSON serialization.

Export

Serialization produces a mostly specification compliant JSON with a few minor differences:

  • not all implicit elements are constructed which will fail for attributes that are defined as non-null in the standard JSON schema, e.g. Function::result;

  • references can be serialized with relative @uri field for references to elements from other documents by passing include_cross_ref_uris=True (default) to json.dumps. This brings JSON exports in-line with corresponding XMI exports used by the Pilot implementation where references are always exported as <relative URI>#<element id>. Additionally, such exports enable much faster deserialization because cross references are transparent and do not require searching the world for their resolution.

    Note

    A custom URI scheme to reference elements relative to their owning SysML packages (not the elements) may be added in the future as package manager support is implemented. This would remove the dependence on filesystem layout for deserialization.

json: str = syside.json.dumps(
    element, options=syside.SerializationOptions.minimal()
)

SerializationOptions controls what gets serialized. Using minimal is recommended for most use cases.

Warning

JSONs typically take 100-1000 times more space than the original textual notation even with minimal options and are opaque to human readers. Textual notation is recommended instead of JSON whenever possible.

Import

JSON deserialization (import) works on the same JSON files as were exported. However, in the interest of keeping the initial implementation simple there are a few limitations:

  • root node is inferred as:

    • the first Namespace without an owning relationship,

    • the last ancestor of the first element in the array following either owning namespaces, owning related elements, or owning relationships,

    • the first element in the array otherwise;

  • references to elements from other Documents may contain a @uri field. The URI of the Document model is deserialized into will be used to resolve relative URI references, otherwise an empty URI will be passed to the resolver callback;

  • deserialization may be lossy because the specification dumps all owned elements into owned_relationships and owned_related_elements attributes which lose the more fine grained information stored in the model by Syside. For example, SendActionUsage receiver, payload, and sender are all parameters to ReferenceUsage, only disambiguated by their relative position, so if one is missing the others may be deserialized into different members.

Note that deserialization ignores majority of fields present in the JSON schema, including all derived fields with the exception of name and shortName. Therefore users may wish to export JSONs with minimal export options to reduce memory usage and improve performance.

Currently, to support cyclical JSON imports, foreign references are not resolved eagerly and instead must be resolved after deserialization:

result, document = syside.json.loads(
    model_json, "file:///home/user/test.sysml"
)
# ... collect all valid element ids for linking, e.g.
map = syside.IdMap()
map.insert_or_assign(result.document)  # and other documents
# resolve pending foreign references
report, success = result.link(map)

DeserializedModel.link accepts any callable (uri: str, element_id: UUID) -> Element | None, not just IdMap. Note that for unowned references without @uri field, uri will be an empty string.

Low-level

High-level json.dumps and json.loads are built on top of low-level API for convenience. Performance of repeated JSON serializations can be improved using low-level API: