8.2Profile Validator

 

8.2.1Validator Modules

 

HAPI provides a built-in and configurable mechanism for validating resources. This mechanism is called the Resource Validator.

The resource validator is an extendible and modular system, and you can configure it in a number of ways in order to get the specific type of validation you want to achieve.

The validator can be manually invoked at any time by creating a validator and configuring it with one or more IValidatorModule instances.

FhirContext ctx = FhirContext.forR4();

// Ask the context for a validator
FhirValidator validator = ctx.newValidator();

// Create a validator modules and register it
IValidatorModule module = new FhirInstanceValidator();
validator.registerValidatorModule(module);

// Pass a resource in to be validated. The resource can
// be an IBaseResource instance, or can be a raw String
// containing a serialized resource as text.
Patient resource = new Patient();
ValidationResult result = validator.validateWithResult(resource);
String resourceText = "<Patient.....>";
ValidationResult result2 = validator.validateWithResult(resourceText);

// The result object now contains the validation results
for (SingleValidationMessage next : result.getMessages()) {
   System.out.println(next.getLocationString() + " " + next.getMessage());
}
Note that in earlier releases of HAPI FHIR it was common to register different kinds of validator modules (such as [Schema/Schematron](./schema_validator.html)) because the FHIR Instance Validator module described below was not mature. This is no longer the case, and it is generally recommended to use the FHIR Instance Validator.

8.2.2FHIR Conformance Packages

 

There are a few key concepts worth explaining before getting into how validation is performed in HAPI FHIR.

Conformance Resources:

  • StructureDefinition – Contains definitions of the valid fields in a given resource, including details about their datatypes, min/max cardinalities, valid values, and other rules about what content is valid and what is not. StructureDefinition resources are also used to express derivative profiles (e.g. a description of a constraint on a FHIR resource for a specfic purpose) as well as to describe extensions.

  • CodeSystem – Contains definiitions of codes and vocabularies that can be used in FHIR resources, or even outside of FHIR resources.

  • ValueSet – Contains lists of codes drawn from one or more CodeSystems that are suitable for use in a specific field in a FHIR resource.

8.2.3FHIR Instance Validator

 

HAPI has very complete support for validation against FHIR conformance resources.

This functionality is proviided by the HAPI FHIR "reference validator", which is able to check a resource for conformance to FHIR profiles.

The FHIR instance validator is very powerful. It will use terminology services to validate codes, StructureDefinitions to validate semantics, and uses a customized XML/JSON parser in order to provide descriptive error messages.

It is always worth considering the performance implications of using the Instance Validator at runtime in a production system. While efforts are made to keep the Instance Validator and its supporting infrastructure performant, the act of performing deep semantic validation is never going to be without some performance cost.

The FHIR instance validator can be used to validate a resource against the official structure definitions (produced by HL7) as well as against custom definitions provided either by HL7 or by the user.

8.2.4Running the Validator

 
Note on FHIR Versions: Many of the classes described on this page have multiple versions, and you should use the version of the class the is appropriate for the version of FHIR you are looking to validate. For example, the examples and links below are using the org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator class to validate FHIR R4 resources, but you would want to use the class org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator if you need to validate DSTU3 content.

To execute the validator, you simply create an instance of FhirInstanceValidator and register it to new validator, as shown in the example below.

Note that the example below uses the official FHIR StructureDefintions and ValueSets to validate the resource. It will not work unless you include the hapi-fhir-validation-resources-[version].jar module/JAR on your classpath.

FhirContext ctx = FhirContext.forR4();

// Create a FhirInstanceValidator and register it to a validator
FhirValidator validator = ctx.newValidator();
FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
validator.registerValidatorModule(instanceValidator);

/*
 * If you want, you can configure settings on the validator to adjust
 * its behaviour during validation
 */
instanceValidator.setAnyExtensionsAllowed(true);


/*
 * Let's create a resource to validate. This Observation has some fields
 * populated, but it is missing Observation.status, which is mandatory.
 */
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("http://loinc.org").setCode("12345-6");
obs.setValue(new StringType("This is a value"));

// Validate
ValidationResult result = validator.validateWithResult(obs);

/*
 * Note: You can also explicitly declare a profile to validate against
 * using the block below.
 */
// ValidationResult result = validator.validateWithResult(obs, new ValidationOptions().addProfile("http://myprofile.com"));

// Do we have any errors or fatal errors?
System.out.println(result.isSuccessful()); // false

// Show the issues
for (SingleValidationMessage next : result.getMessages()) {
   System.out.println(" Next issue " + next.getSeverity() + " - " + next.getLocationString() + " - " + next.getMessage());
}
// Prints:
// Next issue ERROR - /f:Observation - Element '/f:Observation.status': minimum required = 1, but only found 0
// Next issue WARNING - /f:Observation/f:code - Unable to validate code "12345-6" in code system "http://loinc.org"

// You can also convert the result into an operation outcome if you 
// need to return one from a server
OperationOutcome oo = (OperationOutcome) result.toOperationOutcome();

8.2.5Supplying Your Own Definitions

 

The FhirInstanceValidator relies on an implementation of an interface called IValidationSupport interface to load StructureDefinitions, validate codes, etx.

By default, an implementation of this interface called DefaultProfileValidationSupport is used. This implementation simply uses the built-in official FHIR definitions to validate against (and in many cases, this is good enough).

However, if you have needs beyond simply validating against the core FHIR specification, you may wish to use something more.

FhirContext ctx = FhirContext.forR4();

// Create a PrePopulatedValidationSupport and load it with our custom structures
PrePopulatedValidationSupport prePopulatedSupport = new PrePopulatedValidationSupport();

// In this example we're loading two things, but in a real scenario we might
// load many StructureDefinitions, ValueSets, CodeSystems, etc.
prePopulatedSupport.addStructureDefinition(someStructureDefnition);
prePopulatedSupport.addValueSet(someValueSet);

// We'll still use DefaultProfileValidationSupport since derived profiles generally
// rely on built-in profiles also being available
DefaultProfileValidationSupport defaultSupport = new DefaultProfileValidationSupport();

// We'll create a chain that includes both the pre-populated and default. We put
// the pre-populated (custom) support module first so that it takes precedence
ValidationSupportChain supportChain = new ValidationSupportChain();
supportChain.addValidationSupport(prePopulatedSupport);
supportChain.addValidationSupport(defaultSupport);

// Create a validator using the FhirInstanceValidator module. We can use this
// validator to perform validation
FhirInstanceValidator validatorModule = new FhirInstanceValidator(supportChain);
FhirValidator validator = ctx.newValidator().registerValidatorModule(validatorModule);
ValidationResult result = validator.validateWithResult(input);

8.2.6Built-In Validation Support Classes

 

There are a several implementations of the IValidationSupport interface built into HAPI FHIR that can be used, typically in a chain.

  • DefaultProfileValidationSupport - Supplies the built-in FHIR core structure definitions, including both structures and vocabulary.

  • ValidationSupportChain - Can be used to chain multiple implementations together so that for every request, each support class instance in the chain is tried in sequence.

  • PrePopulatedValidationSupport - Contains a series of HashMaps that store loaded conformance resources in memory. Typically this is initialized at startup in order to add custom conformance resources into the chain.

  • PrePopulatedValidationSupport - Contains a series of HashMaps that store loaded conformance resources in memory. Typically this is initialized at startup in order to add custom conformance resources into the chain.

  • CachingValidationSupport - Caches results of calls to a wrapped service implementation for a period of time. This class can be a significant help in terms of performance if you are loading conformance resources or performing terminology operations from a database or disk.

  • SnapshotGeneratingValidationSupport - Generates StructureDefinition snapshots as needed. This should be added to your chain if you are working wiith differential StructueDefinitions that do not include the snapshot view.