openehr 0.3.0

openEHR Reference Model types, validation, paths, AQL parsing, and change-control security primitives
Documentation
# 9. Serialization

Requirement prefix: `J9`.

openEHR canonical JSON (ITS-JSON) is what every openEHR implementation
exchanges. A type that serializes but does not read back cannot leave the
process.

## Round trip

- **J9.1** Every modelled type MUST round-trip: build → serialize → deserialize
  → compare equal.
- **J9.2** A second round trip MUST be byte-identical to the first. A serializer
  that changes the bytes on the second pass normalised something on the first,
  and normalising a clinical record silently is what must not happen.
- **J9.3** Partial dates, negative durations, fractional seconds, and explicit
  UTC offsets MUST survive verbatim (`D3.9`, `D3.10`).

## `_type`

- **J9.4** `_type` MUST be emitted wherever the declared attribute type is
  abstract or has subclasses, and on classes exchanged standalone —
  `COMPOSITION`, `EHR_STATUS`, `EHR_ACCESS`, `FOLDER`, `HISTORY`,
  `EVENT_CONTEXT`, `ACTIVITY`, `ISM_TRANSITION`, `INSTRUCTION_DETAILS`.
- **J9.5** `_type` MUST be **required** on input for `DATA_VALUE`. Guessing is
  not available: `{"value": "P1D"}` is a syntactically valid `DV_TEXT`,
  `DV_URI`, `DV_DATE`, and `DV_DURATION`, and picking one would turn a
  measurement into a string in a way nothing downstream could detect.
- **J9.6** Where `_type` is absent and the choice **is** decidable by shape, the
  crate MUST decide by the attribute that carries meaning and MUST NOT lose it:
  `defining_code` for `DV_TEXT` versus `DV_CODED_TEXT` (`M5.4`), `relationship`
  for `PARTY_RELATED` (`M5.13`), `::` count for `UID_BASED_ID` (`I2.20`).
- **J9.7** A `_type` naming a different class than the declared abstract type
  admits MUST be an error. A `_type` on a concrete class MAY be ignored, because
  there it is redundant.

## Reading is lenient, writing is canonical

- **J9.8** The crate MUST accept identifiers written as bare strings where
  openEHR specifies `{"value": …}` objects. Template tooling emits them, and
  rejecting them converts a cosmetic divergence into an import failure.
- **J9.9** The crate MUST ignore attributes it does not model rather than
  rejecting the document. openEHR adds attributes between minor releases, and a
  strict reader rejects tomorrow's payload for containing something it does not
  need.
- **J9.10** The crate MUST always **write** the canonical form, so that leniency
  normalises on round trip rather than propagating.
- **J9.11** Null and empty-list attributes MUST be omitted on output, as
  ITS-JSON requires.

## Canonical bytes for digests

- **J9.12** A canonical byte form MUST be defined for hashing (§11): object keys
  sorted by Unicode scalar value, no insignificant whitespace, array order
  preserved.
- **J9.13** Numbers MUST NOT be renormalised in the canonical form. Measured
  precision is data.
- **J9.14** Key ordering MUST NOT depend on locale. A locale-dependent digest is
  not reproducible across hosts.

## Recursion

- **J9.15** Deserialization is recursive and its stack cost is a function of
  document depth and build profile. The crate MUST document this and MUST NOT
  silently impose a depth limit (`S1.15`). The measured requirement and the
  method of measuring it are recorded in [`audit.md`]audit.md.