zen.fhir
is a zen-lang namespace that is used in all packages generated by zen-fhir tool.
zen.fhir
extends zen/schema
to allow describing FHIR-specific info such as bindings, references, extensions, etc.
- About
- How to
This section contains examples of describing FHIR constraints with zen.
:import
.
Another common thing is :zen.fhir/version
property of schemas.
It defines which zen.fhir
syntax version your schema will use.
Currently, schemas are required to specify the version and the current version is :zen.fhir/version "0.5.0"
.
In the future, this property may be deprecated in a backward-compatible way.
Create a schema with the zen.fhir/profile-schema
tag. The schema must contain :zen.fhir/profileUri
,
which will be referenced by resources with meta.profile
property.
Here’s an example of a profile on the Patient
resource type:
{:ns MyProfiles
:import #{zen.fhir hl7-fhir-r4-core.Patient}
MyPatientProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
:zen.fhir/version "0.5.0"
:zen.fhir/profileUri "urn:fhir:extension:MyPatientProfile"
:confirms #{hl7-fhir-r4-core.Patient/schema}}}
Note the :confirms #{hl7-fhir-r4-core.Patient/schema}
part: it means that the data which will be
validated by your schema also should be validated by the base Patient schema.
ℹ️ Note that in this case hl7-fhir-r4-core.Patient
was added to the :import
.
This is done in the same way as adding a profile on a existing FHIR resource type but you need to set :confirms
to a profile instead of a base schema
MyPatientProfileOnAProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
:zen.fhir/version "0.5.0"
:zen.fhir/profileUri "urn:fhir:extension:MyPatientProfileOnAProfile"
:confirms #{MyPatientProfile}}
When you have a symbol with the zen.fhir/profile-schema
tag you can describe constraints.
The important thing to keep in mind is that :confirms
property is not an inheritance.
Your schema doesn’t implicitly receive any properties from schemas mentioned in :confirms
.
The effect of this is that to describe a constraint you need to explicitly describe some data types and your data structure.
The next example will illustrate this.
In zen-lang to require a key to be present you need to set a :require
property.
Here’s the updated example of the profile on the Patient resource type with :active
and :name
keys required:
MyPatientProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
:zen.fhir/version "0.5.0"
:zen.fhir/profileUri "urn:fhir:extension:MyPatientProfile"
:confirms #{hl7-fhir-r4-core.Patient/schema}
:type zen/map
:require #{:active :name}}
Note that we had to describe the :type zen/map
to use the :require
property.
Your schema doesn’t implicitly inherit the :type zen/map
from the :confirms #{hl7-fhir-r4-core.Patient/schema}
.
And because :require
is a property of the zen/map
type you need to specify the type explicitly in your schema.
Currently, there’s no way to forbid using an element in zen-lang. Here’s the issue to track the status of this feature.
:type zen/vector
provides :minItems
and :maxItems
properties. Example of limiting a Patient.name
to exactly one element:
MyPatientProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
:zen.fhir/version "0.5.0"
:zen.fhir/profileUri "urn:fhir:extension:MyPatientProfile"
:confirms #{hl7-fhir-r4-core.Patient/schema}
:type zen/map
:require #{:name}
:keys {:name {:type zen/vector
:minItems 1
:maxItems 1}}}
Setting that a zen/vector
has a minimum 1 element does not make a key that stores this value required to be present.
We need to explicitly state that the key is also required.
In FHIR profiles pattern[x]
most of the time is used in slicings.
:match
property of zen/schema
allows to describe a pattern matching.
Below is an example defining a pattern to the Observation.code
element,
the pattern describes that in the :coding
array should be
at least one object with :system "my-system"
and :code "my-code"
:
MyObservationProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
:zen.fhir/version "0.5.0"
:zen.fhir/profileUri "urn:fhir:extension:MyObservationProfile"
:confirms #{hl7-fhir-r4-core.Observation/schema}
:type zen/map
:keys {:code {:match {:coding #{{:system "my-system", :code "my-code"}}}}}}
:match
pattern matching syntax is a recursive data structure that consists of several parts:
{}
contains keys and their patterns applied to an object, each key from the{}
should be present in the object and match the pattern. The object may contain any extra keys not mentioned in the pattern.#{}
contains patterns applied to arrays, for each pattern from the#{}
there should be at least one match in a data array. The array may contain any other elements not matched by the pattern.- any other primitive value means that data should be a constant value
Break down of the example above:
- The pattern is
{:coding #{{:system "my-system", :code "my-code"}}}
; - Top level of the pattern is
{:coding ...}
, it expects data to be an object containing key:coding
; - To a value of the key
:coding
the pattern#{{:system ...}}
is applied; - The
#{}
syntax expects the data to be an array containing at least one match to the pattern{:system "my-system", :code "my-code"}
; {:system "my-system", :code "my-code"}
expects data to be an object containing keys:system
and:code
with values “my-system” and “my-code” respectively.
To define a fixed value use :const
property of zen/schema
allows to describe a constant value.
The same way as patterns, value[x]
most of the times is used in slicings and also to set an extension URL.
Zen FHIR offers first-class extensions instead, in case you want to define an extension refer to the extension example.
Here’s an example setting a value of the Observation.status
element to be always final
if it is present:
MyObservationProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
:zen.fhir/version "0.5.0"
:zen.fhir/profileUri "urn:fhir:extension:MyObservationProfile"
:confirms #{hl7-fhir-r4-core.Observation/schema}
:type zen/map
:keys {:status {:const {:value "final"}}}}
When you have a schema with the zen.fhir/profile-schema
tag you can describe reference to another schema. Use :zen.fhir/reference
property and describe set of profile names in :refers
property. In this example, we have described the :managingOrganization
field, which can only refer to the MyOrganizationProfileName
profile.
MyPatientProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
:zen.fhir/version "0.5.0"
:zen.fhir/profileUri "urn:fhir:extension:MyPatientProfile"
:confirms #{hl7-fhir-r4-core.Patient/schema}
:type zen/map
:keys {:managingOrganization {:confirms #{hl7-fhir-r4-core.Reference/schema zen.fhir/Reference}
:zen.fhir/reference {:refers #{MyOrganizationProfileName}}}}}
MyOrganizationProfileName
{:zen/tags #{zen/schema zen.fhir/profile-schema}
:zen.fhir/version "0.5.0"
:zen.fhir/profileUri "urn:fhir:extension:MyOrganizationProfileName"
:confirms #{hl7-fhir-r4-core.Organization/schema}}
You can define slicing in your schema with :slicing
property which allows you to validate particular elements of vector using separate shemas for each element cathegory. To specify validation rules of single slice describe it under :slices
property. Note that each slice should have :filter
property which should contains :match
property. Element becomes part of particular slice if specified data in :match
property matches with element data.
MyObservation
{:zen/tags #{zen.fhir/profile-schema zen/schema},
:zen.fhir/profileUri "urn:fhir:Observation"
:zen.fhir/version "0.5.0"
:confirms #{hl7-fhir-r4-core.Observation/schema}
:type zen/map
:keys {:code {:confirms #{hl7-fhir-r4-core.CodeableConcept/schema}
:type zen/map
:keys {:coding
{:slicing
{:type zen/vector
:every {:confirms #{hl7-fhir-r4-core.Coding/schema}
:slices {"HeartRateCode"
{:schema {:type zen/vector
:minItems 1
:maxItems 1
:every {:type zen/map
:keys {:system {:const {:value "http://loinc.org"}
:confirms #{hl7-fhir-r4-core.uri/schema}}
:code {:const {:value "8867-4"}
:confirms #{hl7-fhir-r4-core.code/schema}}
:require #{:system :code}}}}
:filter {:engine :match,
:match {:code "8867-4", :system "http://loinc.org"}}}}}}}}}}}
When you have a schema with the zen.fhir/profile-schema
tag you can specify value-set. Use zen.fhir/value-set
property and describe existing value-set name in :symbol
property. You also need to specify one of the supported expansions of value-set in :strength
property.
Supported expansions:
:required
To be conformant, the concept in this element SHALL be from the specified value set.:extensible
To be conformant, the concept in this element SHALL be from the specified value set if any of the codes within the value set can apply to the concept being communicated. If the value set does not cover the concept (based on human review), alternate codings (or, data type allowing, text) may be included instead.:preferred
Instances are encouraged to draw from the specified codes for interoperability purposes but are not required to do so to be considered conformant.:example
Instances are not expected or even encouraged to draw from the specified value set. The value set merely provides examples of the types of concepts intended to be included.
MyPatientProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
:zen.fhir/version "0.5.0"
:zen.fhir/profileUri "urn:fhir:extension:MyPatientProfile"
:confirms #{hl7-fhir-r4-core.Patient/schema}
:type zen/map
:keys {:gender {:confirms #{hl7-fhir-r4-core.code/schema},
:zen.fhir/value-set {:symbol hl7-fhir-r4-core.administrative-gender/schema,
:strength :required}}
As it was mentioned before, there’s no inheritance in zen-lang, thus to describe a constraint of a
nested element you need to describe structure containing the element.
The following example requires Patient.name.given
to be present and contain at least one element:
MyPatientProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
:zen.fhir/version "0.5.0"
:zen.fhir/profileUri "urn:fhir:extension:MyPatientProfile"
:confirms #{hl7-fhir-r4-core.Patient/schema}
:type zen/map
:require #{:name}
:keys {:name {:type zen/vector
:minItems 1
:every {:type zen/map
:require #{:given}
:keys {:given {:type zen/vector
:minItems 1}}}}}}
Zen FHIR offers first-class extensions approach instead of regular FHIR way via slicings.
First-class extensions are described in the same way as any other attributes and constraints, but with addition of an extension url.
The following example describes the us-core-race
first-class extension right inside of a profile:
MyPatientProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
:zen.fhir/version "0.5.0"
:zen.fhir/profileUri "urn:fhir:extension:MyPatientProfile"
:confirms #{hl7-fhir-r4-core.Patient/schema}
:type zen/map
:keys {:race
{:type zen/map
:zen/desc "US Core Race Extension",
:fhir/extensionUri "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race"
:require #{:text}
:keys {:ombCategory {:type zen/vector
:maxItems 5
:every {:confirms #{hl7-fhir-r4-core.Coding/schema}
:zen/desc "American Indian or Alaska Native|Asian|Black or African American|Native Hawaiian or Other Pacific Islander|White"
:zen.fhir/value-set {:symbol omb-race-category-value-set
:strength :required}}}
:detailed {:type zen/vector
:every {:confirms #{hl7-fhir-r4-core.Coding/schema}
:zen/desc "Extended race codes"
:zen.fhir/value-set {:symbol detailed-race-value-set
:strength :required}}}
:text {:confirms #{hl7-fhir-r4-core.string/schema}
:zen/desc "Race Text"}}}}}
Note that extension elements have :confirms
to a FHIR primitive or complex type specified.
Previously these were specified in the base profile schema.
These :confirms
and the :fhir/extensionUri
are needed to allow zen FHIR <-> FHIR format transformation
The structure defined by this schema describes data of such shape:
resourceType: Patient
id: sample-pt
race:
category:
- {system: 'urn:oid:2.16.840.1.113883.6.238', code: 2028-9, display: Asian}
detailed:
- {system: 'urn:oid:2.16.840.1.113883.6.238', code: 2029-7, display: Asian Indian}
text: Asian Indian
This then can be converted to FHIR format and result in this
resourceType: Patient
id: sample-pt
extension:
- url: http://hl7.org/fhir/us/core/StructureDefinition/us-core-race
extension:
- url: ombCategory
valueCoding: {system: 'urn:oid:2.16.840.1.113883.6.238', code: 2028-9, display: Asian}
- url: detailed
valueCoding: {system: 'urn:oid:2.16.840.1.113883.6.238', code: 2029-7, display: Asian Indian}
- url: text
valueString: Asian Indian
Extensions can be extracted to a separate schema if you’re going to reuse them across different profiles.
Here’s the us-core-race
profile updated in a such manner:
us-core-race
{:zen/tags #{zen/schema zen.fhir/structure-schema}
:zen.fhir/version "0.5.0"
:zen.fhir/profileUri "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race"
:type zen/map
:require #{:text}
:keys {:ombCategory {:type zen/vector
:maxItems 5
:every {:confirms #{hl7-fhir-r4-core.Coding/schema}
:zen/desc "American Indian or Alaska Native|Asian|Black or African American|Native Hawaiian or Other Pacific Islander|White"
:zen.fhir/value-set {:symbol omb-race-category-value-set
:strength :required}}}
:detailed {:type zen/vector
:every {:confirms #{hl7-fhir-r4-core.Coding/schema}
:zen/desc "Extended race codes"
:zen.fhir/value-set {:symbol detailed-race-value-set
:strength :required}}}
:text {:confirms #{hl7-fhir-r4-core.string/schema}
:zen/desc "Race Text"}}}
MyPatientProfile
{:zen/tags #{zen/schema zen.fhir/profile-schema}
:zen.fhir/version "0.5.0"
:zen.fhir/profileUri "urn:fhir:extension:MyPatientProfile"
:confirms #{hl7-fhir-r4-core.Patient/schema}
:type zen/map
:keys {:race {:confirms #{us-core-race}
:zen/desc "US Core Race Extension"
:fhir/extensionUri "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race"}}}
More complex example of an extension:
MyPatient
{:zen/tags #{zen/schema zen.fhir/profile-schema}
:zen/desc "Patient resource schema with first-class extension definition examples"
:zen.fhir/version "0.5.20"
:confirms #{zen.fhir/Resource}
:zen.fhir/type "Patient"
:zen.fhir/profileUri "urn:profile:MyPatientProfile"
:type zen/map
:keys {:meta {:type zen/map
:keys {:tenant-id
{:confirms #{hl7-fhir-r4-core.string/schema}
:zen/desc "Patient.meta.tenant-id first-class extension"
:fhir/extensionUri "http://tenant-id-extension-url"}}}
:form {:type zen/vector
:zen/desc "Patient.form.[*] array first-class extension"
:every {:confirms #{hl7-fhir-r4-core.uri/schema}
:fhir/extensionUri "http://patient-form-url"}}
:info {:type zen/map
:zen/desc "Patient.info nested first-class extension"
:fhir/extensionUri "http://patient-info"
:keys {:registration {:confirms #{hl7-fhir-r4-core.dateTime/schema}
:zen/desc "Patient.info.registration
extension.url deduced from key"}
:referral {:confirms #{hl7-fhir-r4-core.uri/schema}
:fhir/extensionUri "http://patient-info-referral"
:zen/desc "Patient.info.referral
extension.url is specified"}}}}}