FHIR Model Guide
Overview
The FHIR model component provides Java APIs for parsing, building, generating and validating FHIR resources. Java model classes that represent FHIR resources and data types are generated directly from the structure definitions distributed with the spec. All model objects are thread-safe and immutable. Each model class implements the Java builder pattern (Effective Java, Joshua Bloch) and the visitor pattern (GoF). The classes also implement Java equals, hashCode and toString methods. All date/time processing is done using the Java 8 time library.
Many of the data type classes include additional factory methods to facilitate object construction for common use cases. The model includes generated Javadoc comments complete with excerpts taken directly from the specification. Model classes also include Java annotations for constraints (@Constraint
), required elements (@Required
), choice element types (@Choice
) and value set bindings (@Binding
). Value set bindings are implemented using Code subclasses with constant fields and nested enumerations. Backbone elements are implemented as Java nested classes to keep them organized.
All schema-level (structure, cardinality, value domain) and global (empty resource, empty element) constraint validation is happens during object construction. This means that it is virtually impossible to build a schema invalid FHIR resource using the APIs. Additional constraint validation (invariants, profile, terminology) is performed using the FHIRValidator class. FHIRParser and FHIRGenerator classes are used to parse and generate both JSON and XML formats. FHIRPathEvaluator is a FHIRPath evaluation engine built on an ANTLR4 generated parser. It implements are large portion of the FHIRPath specification and is used for validation and search parameter value extraction.
Building a Resource using the FHIR Model API
The FHIR model API implements the Builder pattern for constructing Resource instances.
Observation bodyWeight = Observation.builder().meta(Meta.builder().profile(Canonical.of("http://hl7.org/fhir/StructureDefinition/bodyweight")).build()).status(ObservationStatus.FINAL).effective(DateTime.builder().value("2019-01-01").build()).category(CodeableConcept.builder()
In the example above, a number of different builder classes are used:
Observation.Builder
DateTime.Builder
CodeableConcept.Builder
Quantity.Builder
Every type in the model that represents a FHIR resource or element has a corresponding nested, static Builder class used for constructing thread-safe, immutable instances.
Several static factory / utility methods are also used:
Canonical.of(...)
Uri.of(...)
Code.of(...)
String.string(...)
(via static import)
Many of the primitive data types contain this type of “helper” method.
Fields from an immutable model object may be copied back into a builder object using the toBuilder()
method:
bodyWeight = bodyWeight.toBuilder().value(bodyWeight.getValue().as(Quantity.class).toBuilder().value(Decimal.of(210)).build()).build();
Parsing a Resource from an InputStream or Reader
// Parse from InputStreamInputStream in = getInputStream("JSON/bodyweight.json");Observation observation = FHIRParser.parser(Format.JSON).parse(in);// Parse from ReaderReader reader = getReader("JSON/bodyweight.json");Observation observation = FHIRParser.parser(Format.JSON).parse(reader);
Generating JSON and XML formats from a Resource instance
// Generate JSON formatFHIRGenerator.generator(Format.JSON).generate(bodyWeight, System.out);// Generate XML formatFHIRGenerator.generator(Format.XML).generate(bodyWeight, System.out);
The FHIRGenerator
interface has a separate factory method that takes boolean prettyPrinting
as a parameter:
// Generate JSON format (with pretty printing)FHIRGenerator.generator(Format.JSON, true).generate(bodyWeight, System.out);
Validating a Resource instance
Schema-level validation occurs during object construction. This includes validation of cardinality constraints and value domains. Additional validation of constraints specified in the model is performed using the FHIRValidator
class.
Observation observation = getObservation();List<Issue> issues = FHIRValidator.validator().validate(observation);for (Issue issue : issues) {if (IssueSeverity.ERROR.equals(issue.getSeverity())) {// handle error}}
Evaluating FHIRPath expressions on a Resource instance
EvaluationContext evaluationContext = new EvaluationContext(bodyWeight);Collection<FHIRPathNode> result = FHIRPathEvaluator.evaluator().evaluate(evaluationContext, "Observation.value.as(Quantity).value >= 200");assert(FHIRPathUtil.isTrue(result));
The EvaluationContext
class builds a FHIRPathTree
from a FHIR resource or element. A FHIRPathTree
is a tree of labeled nodes that wrap FHIR elements and are used by the FHIRPath evaluation engine (FHIRPathEvaluator
).