Skip to main content
Docs/Work with resources
On this page ⌄
Usage guide

Work with resources

Creating, modifying, and reading FHIR resources in Kotlin, using builders, primitives, choice types, and coded values.

Goal and scope​

How the generated model behaves once you move past a first example. This page covers modifying immutable resources, FHIR's variable-precision primitives, choice elements, and coded fields. Examples use R4, and the R4B and R5 packages follow the same shapes.

Resources are immutable, so copy or build​

Every generated class is an immutable Kotlin data class. For shallow changes, copy() with named arguments is enough.

val updated = patient.copy(
id = "patient-02",
birthDate = Date(value = FhirDate.fromString("1990-06-15")),
)

For deeper mutations, such as appending to lists or editing nested elements, every class has a nested Builder. toBuilder() converts the whole tree to builders, you mutate, then build() produces a new immutable instance.

import dev.ohs.fhir.model.r4.String as FhirString

val updated = patient.toBuilder().apply {
name.add(
HumanName.Builder().apply {
given.add(FhirString.Builder().apply { value = "Jane" })
}
)
}.build()

Builders can also construct resources from scratch (Patient.Builder().apply { ... }.build()) when constructor calls nest too deeply to read well.

FHIR primitives are wrapped types​

A FHIR primitive is more than its value, since it can carry an id and extensions. The model therefore wraps every primitive in a class named after the FHIR type. The FHIR string type is dev.ohs.fhir.model.r4.String, boolean is dev.ohs.fhir.model.r4.Boolean, and so on. Since those names shadow Kotlin's built-ins, import them with an alias such as import dev.ohs.fhir.model.r4.String as FhirString. Three primitives get hand-written types because Kotlin's standard types cannot represent them faithfully.

  • FhirDate is a date at year, year-month, or full-date precision, as FHIR allows. Construct from text with FhirDate.fromString("2000-01").
  • FhirDateTime supports the same partial precisions plus full date-time precision with a required UTC offset.
  • FhirDecimal is a precision-preserving decimal backed by a multiplatform big-decimal implementation. It emits the exact lexical form it was parsed from.

Choice types are sealed interfaces​

An element like Patient.deceased[x], which the spec allows as a boolean or a dateTime, is generated as a sealed interface with one implementation per allowed type.

import dev.ohs.fhir.model.r4.Boolean as FhirBoolean

val deceased: Patient.Deceased = Patient.Deceased.Boolean(FhirBoolean(value = false))

// Reading back. The as* accessors return null for the other branches.
val deceasedFlag: FhirBoolean? = deceased.asBoolean()?.value

The compiler rejects types the spec does not allow for the element, and a when over the sealed interface is exhaustive.

Coded fields​

Where the spec binds an element to a required ValueSet, the model generates an enum. Bindings shared across resources live in the terminologies subpackage (for example AdministrativeGender), and bindings used by a single element are nested in the owning class. Three ValueSets are deliberately not generated as enums because they are unbounded or too large to compile (mimetypes, all-languages, and the usage-context set). Those fields stay as plain coded types.

Documentation in the IDE​

Every generated class and property carries the FHIR specification's own definition and comments as KDoc, so the spec text is available on hover in the editor.

Next step​

Serialize and deserialize covers the JSON layer and its configuration limits.