Semantics of `Void`

Hello everyone,
I have a quick question regarding Void and unintialised fields in OpenEHR specification. as I am working on custom serializer and deserializer. that first loads data in-memory to validate then serialize again.

  • Does the concept of Void apply universally to all values to indicate a missing state, or is it only used for object references (like a null pointer)?
  • for primitive data types(like Integer, Real, Double etc), what is their default state at construction time? Do they default to 0 / 0.0, or do they default to an unassigned Void state?

for languages like Java, C# they set primitives to defaults like 0, false. This would allow me also to know if:

  • when is_nullable: True for an prop of type Integer does it mean it can have Void state, or is it just null
  • Furthermore, this knowledge heavily impacts serialization and database storage. If primitives default to zero by spec definition, a serializer can safely drop fields like magnitude: 0 entirely to reduce disk space and traffic size, knowing the deserializer will naturally reconstruct the zero.

I am Sorry if this is a basic question, but I am looking for clarity based strictly on the openEHR specification itself, rather than just how current implementations happen to handle it (which are often influenced by host-language defaults like Java).

Those types should rely on your technology of preference since are not openEHR types, but assumed types that should be provided by the underlying programming language.

I would avoid using 0 / 0.0 as a default for primitive number types. In Java a wrapper classes can be used Integer instead of int. These act as primitive types but can be set to null (Void).

In databases the null (Void) is almost always available for primitive types.

Void means the same as null in e.g. Java; it is object-level.

The spec is written such that primitive types are non-voidable, i.e. they are the same as the Java ‘unboxed’ types like int etc. Each type is assumed to have a default state at creation (unlike in C). Note that String is not a primitive type. An int cannot have a ‘Void’ value.

The spec assumes primitive-valued fields are mandatory. Invariants will tell you if the value matters or not.

This does indeed matter for serialised representation, and you need to create appropriate rules on what it means if a Boolean or Integer field is missing from JSON or XML. Usually you will treat missing Booleans as False and Missing Integers as 0. Then you can avoid storing mainly Booleans that have never been changed from their initial default of False.

Thank you for reponse,
I think the root of my confusion lies in the fact that many current implementations rely on “boxed” primitives (e.g., treating Integer as an object reference rather than primtive), allowing primitive types to naturally hold a null state. Actually, the Foundation Types Specification cross-reference table explicitly maps Integer to java.lang.Integer.

Also @borut.jures also mentioned that default values like 0 or 0.0 should be avoided in favor of wrapper classes (which aligns with the spec mapping). noting that databases natively support null for primitive columns. However, lets not forget that database storage formats—or serialization formats like JSONB—are simply transport and storage representations rather than the underlying formalism itself.

That is why I am asking about it,because as standard behaviour as language agnostic, when deserializing from any of of serialized formalisms (JSON, Yaml, XML ) how should is Void/null handled from OpenEHR point of view internally when validating. rather than leaveing to host languages, i.e if implementation is based on a language without boxed primitives it defaults missing values to 0 (or false for booleans) upon deserialization, it blurs the distinction between a defined zero-value and an absent (Void) value.

example:
for exmple if client side sends

{
    "_type": "Interval<Integer>", 
    "lower": 0,
    "upper": 1
}

can be stored in db as below, and when loaded by any implementation it would load lower as 0 since it is standardized?

{
    "_type": "Interval<Integer>", 
    "upper": 1
}

Another note is that the distinction becomes critical as openEHR moves toward a formal Expression Language (EL). Invariant assertions and expression rules need an explicit, language-agnostic contract defining whether primitive types can evaluate to Void. Without a standardized rule, an expression evaluating a boolean condition like lower > 0 might produce valid logic on a platform using boxed/nullable primitives, but throw a runtime error or yield a false positive on a platform using non-nullable value types. Standardizing this behavior ensures that rules written in EL produce identical semantic results across all execution engines.
what do you think .

These are tricks any implementation is free to use to save space or improve performance; by the time objects are fully materialised however, there should be no missing mandatory elements, which means all primitive properties are populated, as well as all mandatory non-primitive properties.

You just have to make sure your deserialisation layer implements all the rules without error :wink:

You can see how some software uses Jackson heavily to do this sort of thing, also to Stringify more complex fields into single strings, and other such things. Nothing wrong with it, as long as it works.

The EL I now have in the SPLASH framework is an evolution of the EL spec in openEHR, and treats all primitive types as non-voidable. I(f you want voidable Integers and Booleans, you need wrapper types that provide the extra value, which is usually understood as ‘unknown’. I.e. 3-valued Boolean logic. This is unnecessary to have globally, since nearly every Boolean and most if not all Integers are not Voidable; it is only in specific parts of the model that represent data from the external world that may need this facility. Hence ELEMENT.null_flavour.

That’s great, I am really looking forward to SPLASH.

But will it be integrated as parted the OpenEHR spec, or as seperate specification. because it would be amazing if OpenEHR have specified mature expression language to use it everywhere (invariants, ADL, pre&posconditions). Many of the current invariants strings are just hints to implementers not valid EL assertions , like using terminlology instead of type ref {TERMINOLOGY_SERVICE} thus my Interperter can’t run them.

Thank you for your time.

SPLASH has no current status within openEHR; it is a separate project.

You can see the EL meta-model within BMM4 here.

I have not yet opened the relevant source repos, still working on some details.

You can browse the BMML expression of openEHR, and you will see invariants and pre- and post-conditions, all in computable EL.

Example invariants from VERSION_TREE_ID.


invariant
    value_valid: ¬ value.is_empty();
    trunk_version_valid: ∃ trunk_version ∧ trunk_version.is_integer ∧ trunk_version.as_integer >= 1;
    branch_number_valid: ∃ branch_number ⇒ branch_number.is_integer ∧ branch_number.as_integer >= 1;
    branch_version_valid: ∃ branch_version ⇒ branch_version.is_integer ∧ branch_version.as_integer >= 1;
    branch_validity: (¬ ∃ branch_number ∧ ¬ ∃ branch_version) ⊻ (∃ branch_number ∧ ∃ branch_version);
    is_branch_validity: is_branch ⊻ ¬ ∃ branch_number;
    is_first_validity: ¬ is_first ⊻ trunk_version.is_equal("1");