HAPI supportes two types of validation, both of which are described in the sections below.

  • Parser Validation is validation at runtime during the parsing of a resource. It can be used to catch input data that is impossible to fit into the HAPI data model. For example, it can be used to throw exceptions or display error messages if a resource being parsed contains elements for which there are no appropriate fields in a HAPI data structure. This is useful in order to ensure that no data is being lost during parsing, but is less comprehensive than resource validation against raw text data.
  • Resource Validation is validation of the raw or parsed resource against the official FHIR validation rules (e.g. Schema/Schematron/Profile/StructureDefinition/ValueSet) as well as against custom profiles which have been developed.

Parser Validation

Parser validation is controlled by calling setParserErrorHandler(IParserErrorHandler) on either the FhirContext or on individual parser instances. This method takes an IParserErrorHandler , which is a callback that will be invoked any time a parse issue is detected.

There are two implementations of IParserErrorHandler worth mentioning. You can also supply your own implementation if you want.

  • LenientErrorHandler logs any errors but does not abort parsing. By default this handler is used, and it logs errors at "warning" level. It can also be configured to silently ignore issues.
  • StrictErrorHandler throws a DataFormatException if any errors are detected.

The following example shows how to configure a parser to use strict validation.

      FhirContext ctx = FhirContext.forDstu2();
      
      // Create a parser and configure it to use the strict error handler
      IParser parser = ctx.newXmlParser();
      parser.setParserErrorHandler(new StrictErrorHandler());

      // This example resource is invalid, as Patient.active can not repeat
      String input = "<Patient><active value=\"true\"/><active value=\"false\"/></Patient>";

      // The following will throw a DataFormatException because of the StrictErrorHandler
      parser.parseResource(Patient.class, input);

You can also configure the error handler at the FhirContext level, which is useful for clients.

FhirContext ctx = FhirContext.forDstu2();

ctx.setParserErrorHandler(new StrictErrorHandler());

// This client will have strict parser validation enabled
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");

FhirContext level validators can also be useful on servers.

   public class MyRestfulServer extends RestfulServer {

      @Override
      protected void initialize() throws ServletException {
         // ...Configure resource providers, etc... 
         
         // Create a context, set the error handler and instruct
         // the server to use it
         FhirContext ctx = FhirContext.forDstu2();
         ctx.setParserErrorHandler(new StrictErrorHandler());
         setFhirContext(ctx);
      }
      
   }

Resource Validation

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.forDstu2();

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

// Create some modules and register them 
IValidatorModule module1 = new SchemaBaseValidator(ctx);
validator.registerValidatorModule(module1);
IValidatorModule module2 = new SchematronBaseValidator(ctx);
validator.registerValidatorModule(module2);

// 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());
}

Resource Validation Module: Schema/Schematron

FHIR resource definitions are distributed with a set of XML schema files (XSD) as well as a set of XML Schematron (SCH) files. These two sets of files are complimentary to each other, meaning that in order to claim compliance to the FHIR specification, your resources must validate against both sets.

The two sets of files are included with HAPI, and it uses them to perform validation.

Preparation

In order to use HAPI's Schematron support, a libaray called Phloc-Schematron is used, so this library must be added to your classpath (or Maven POM file, Gradle file, etc.) Note that this library is specified as an optional dependency by HAPI FHIR so you need to explicitly include it if you want to use this functionality.

See Downloads for more information on how to add it.

Validating a Resource

To validate a resource instance, a new validator instance is requested from the FHIR Context. This validator is then applied against a specific resource instance, as shown in the example below.

      // As always, you need a context
      FhirContext ctx = FhirContext.forDstu2();

      // Create and populate a new patient object
      Patient p = new Patient();
      p.addName().addFamily("Smith").addGiven("John").addGiven("Q");
      p.addIdentifier().setSystem("urn:foo:identifiers").setValue("12345");
      p.addTelecom().setSystem(ContactPointSystemEnum.PHONE).setValue("416 123-4567");

      // Request a validator and apply it
      FhirValidator val = ctx.newValidator();

      // Create the Schema/Schematron modules and register them. Note that
      // you might want to consider keeping these modules around as long-term
      // objects: they parse and then store schemas, which can be an expensive
      // operation.
      IValidatorModule module1 = new SchemaBaseValidator(ctx);
      IValidatorModule module2 = new SchematronBaseValidator(ctx);
      val.registerValidatorModule(module1);
      val.registerValidatorModule(module2);

      ValidationResult result = val.validateWithResult(p);
      if (result.isSuccessful()) {
         
         System.out.println("Validation passed");
         
      } else {
         // We failed validation!
         System.out.println("Validation failed");
      }
      
      // The result contains a list of "messages" 
      List<SingleValidationMessage> messages = result.getMessages();
      for (SingleValidationMessage next : messages) {
         System.out.println("Message:");
         System.out.println(" * Location: " + next.getLocationString());
         System.out.println(" * Severity: " + next.getSeverity());
         System.out.println(" * Message : " + next.getMessage());
      }
      
      // You can also convert the results into an OperationOutcome resource
      OperationOutcome oo = (OperationOutcome) result.toOperationOutcome();
      String results = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(oo);
      System.out.println(results);

Validating a Set of Files

The following example shows how to load a set of resources from files on disk and validate each one.

      FhirContext ctx = FhirContext.forDstu2();

      // Create a validator and configure it
      FhirValidator validator = ctx.newValidator();
      validator.setValidateAgainstStandardSchema(true);
      validator.setValidateAgainstStandardSchematron(true);

      // Get a list of files in a given directory
      String[] fileList = new File("/home/some/dir").list(new WildcardFileFilter("*.txt"));
      for (String nextFile : fileList) {

         // For each file, load the contents into a string
         String nextFileContents = IOUtils.toString(new FileReader(nextFile));

         // Parse that string (this example assumes JSON encoding)
         IBaseResource resource = ctx.newJsonParser().parseResource(nextFileContents);

         // Apply the validation. This will throw an exception on the first
         // validation failure
         ValidationResult result = validator.validateWithResult(resource);
         if (result.isSuccessful() == false) {
            throw new Exception("We failed!");
         }
         
      }

Resource Validation (Profile/StructureDefinition)

As of HAPI FHIR 1.2, HAPI supports validation against StructureDefinition resources. This functionality uses the HL7 "InstanceValidator", which is able to check a resource for conformance to a given profile (StructureDefinitions and ValueSets), including validating fields, extensions, and codes for conformance to their given ValueSets.

StructureDefinition validation 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.

This style of validation is still experimental, and should be used with caution. It is very powerful, but is still under active development and may continue to change over time.

Preparation

To use this functionality, you must add the following two dependencies to your classpath (or Maven POM file, Gradle file, etc.):

  • hapi-fhir-structures-hl7org-dstu2 : This file contains the "reference implementation" structures and tooling. You need to include it even if you are not using the RI model (the StructureDefinition validation will work against HAPI structures as well)
  • hapi-fhir-validation-resources-dstu2 : This file contains the official FHIR StructureDefinition files, and the ValueSets needed to support them.

See the download page for more information.

Running the Validator

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 to your classpath.

      FhirContext ctx = FhirContext.forDstu2();

      // Create a FhirInstanceValidator and register it to a validator
      FhirValidator validator = ctx.newValidator();
      FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
      validator.registerValidatorModule(instanceValidator);
      
      /*
       * 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 StringDt("This is a value"));
      
      // Validate
      ValidationResult result = validator.validateWithResult(obs);
      
      // 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();

Supplying your own StructureDefinitions

The FhirInstanceValidator relies on the IValidationSupport interface to load StructureDefinitions, and validate codes.

By default, the DefaultProfileValidationSupport implementation is used. This implementation loads the FHIR profiles from the validator resources JAR. If you want to use your own profiles, you may wish to supply your own implementation.

      FhirContext ctx = FhirContext.forDstu2();

      // Create a FhirInstanceValidator and register it to a validator
      FhirValidator validator = ctx.newValidator();
      FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
      validator.registerValidatorModule(instanceValidator);
      
      IValidationSupport valSupport = new IValidationSupport() {
         
         @Override
         public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
            // TODO: Implement
            return null;
         }
         
         @Override
         public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
            // TODO: Implement
            return false;
         }
         
         @Override
         public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
            // TODO: Implement
            return null;
         }
         
         @Override
         public ValueSet fetchCodeSystem(FhirContext theContext, String theSystem) {
            // TODO: Implement
            return null;
         }

         @Override
         public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
            // TODO: Implement
            return null;
         }
      };
      
      /*
       * ValidationSupportChain strings multiple instances of IValidationSupport together. The
       * code below is useful because it means that when the validator wants to load a 
       * StructureDefinition or a ValueSet, it will first use DefaultProfileValidationSupport,
       * which loads the default HL7 versions. Any StructureDefinitions which are not found in
       * the built-in set are delegated to your custom implementation.
       */
      ValidationSupportChain support = new ValidationSupportChain(new DefaultProfileValidationSupport(), valSupport);
      instanceValidator.setValidationSupport(support);
   

Back to top

Reflow Maven skin by Andrius Velykis.