Note on FHIR Versions: Because of the differences in the way the structures work between DSTU2 and DSTU3, we have provided two versions of many of the examples on this page. See the download page for more information on FHIR versions.

Extensions are a key part of the FHIR specification, providing a standardized way of placing additional data in a resource.

The simplest way to interact with extensions (i.e. to add them to resources you are creating, or to read them from resources you are consuming) is to treat them as "undeclared extensions". Undeclared extensions can be added to any of the built in FHIR resource types that come with HAPI-FHIR.

DSTU2

// Create an example patient
Patient patient = new Patient();
patient.addIdentifier().setUse(IdentifierUseEnum.OFFICIAL).setSystem("urn:example").setValue("7000135");

// Create an extension
ExtensionDt ext = new ExtensionDt();
ext.setModifier(false);
ext.setUrl("http://example.com/extensions#someext");
ext.setValue(new DateTimeDt("2011-01-02T11:13:15"));

// Add the extension to the resource
patient.addUndeclaredExtension(ext);

DSTU3

// Create an example patient
Patient patient = new Patient();
patient.addIdentifier().setUse(IdentifierUse.OFFICIAL).setSystem("urn:example").setValue("7000135");

// Create an extension
Extension ext = new Extension();
ext.setUrl("http://example.com/extensions#someext");
ext.setValue(new DateTimeType("2011-01-02T11:13:15"));

// Add the extension to the resource
patient.addExtension(ext);

Undeclared extensions can also be added to datatypes (composite or primitive).

DSTU2

// Continuing the example from above, we will add a name to the patient, and then
// add an extension to part of that name
HumanNameDt name = patient.addName();
name.addFamily().setValue("Shmoe");

// Add a new "given name", which is of type StringDt 
StringDt given = name.addGiven();
given.setValue("Joe");

// Create an extension and add it to the StringDt
ExtensionDt givenExt = new ExtensionDt(false, "http://examples.com#moreext", new StringDt("Hello"));
given.addUndeclaredExtension(givenExt);

DSTU3

// Continuing the example from above, we will add a name to the patient, and then
// add an extension to part of that name
HumanName name = patient.addName();
name.setFamily("Shmoe");

// Add a new "given name", which is of type String 
StringType given = name.addGivenElement();
given.setValue("Joe");

// Create an extension and add it to the String
Extension givenExt = new Extension("http://examples.com#moreext", new StringType("Hello"));
given.addExtension(givenExt);

Sub-Extensions

Extensions may also have child extensions as their content, instead of a datatype. This is done by adding a child undeclared extension to the parent extension.

DSTU2

Patient patient = new Patient();

// Add an extension (initially with no contents) to the resource 
ExtensionDt parent = new ExtensionDt(false, "http://example.com#parent");
patient.addUndeclaredExtension(parent);

// Add two extensions as children to the parent extension
ExtensionDt child1 = new ExtensionDt(false, "http://example.com#childOne", new StringDt("value1"));
parent.addUndeclaredExtension(child1);

ExtensionDt child2 = new ExtensionDt(false, "http://example.com#childTwo", new StringDt("value1"));
parent.addUndeclaredExtension(child2);

DSTU3

Patient patient = new Patient();

// Add an extension (initially with no contents) to the resource 
Extension parent = new Extension("http://example.com#parent");
patient.addExtension(parent);

// Add two extensions as children to the parent extension
Extension child1 = new Extension("http://example.com#childOne", new StringType("value1"));
parent.addExtension(child1);

Extension child2 = new Extension("http://example.com#chilwo", new StringType("value1"));
parent.addExtension(child2);

Retrieving Extension Values

HAPI provides a few ways of accessing extension values in resources which are received from other sources (i.e. downloaded by a client).

DSTU2

// Get all extensions (modifier or not) for a given URL
List<ExtensionDt> resourceExts = patient.getUndeclaredExtensionsByUrl("http://fooextensions.com#exts");

// Get all non-modifier extensions regardless of URL
List<ExtensionDt> nonModExts = patient.getUndeclaredExtensions();

//Get all non-modifier extensions regardless of URL
List<ExtensionDt> modExts = patient.getUndeclaredModifierExtensions();

DSTU3

// Get all extensions (modifier or not) for a given URL
List<Extension> resourceExts = patient.getExtensionsByUrl("http://fooextensions.com#exts");

// Get all non-modifier extensions regardless of URL
List<Extension> nonModExts = patient.getExtension();

//Get all non-modifier extensions regardless of URL
List<Extension> modExts = patient.getModifierExtension();

Custom Resource Types

The most elegant way of adding extensions to a resource is through the use of custom fields. The following example shows a custom type which extends the FHIR Patient resource definition through two extensions.

import java.util.ArrayList;
import java.util.List;

import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.StringType;

import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.util.ElementUtil;

/**
 * Definition class for adding extensions to the built-in
 * Patient resource type.
 * 
 * Note the "profile" attribute below, which indicates the URL/ID of the
 * profile implemented by this resource. You are not required to supply this,
 * but if you do it will be automatically populated in the resource meta
 * tag if the resource is returned by a server.
 */
@ResourceDef(name="Patient", profile="http://example.com/StructureDefinition/mypatient")
public class MyPatient extends Patient {

   private static final long serialVersionUID = 1L;

   /**
	 * Each extension is defined in a field. Any valid HAPI Data Type
	 * can be used for the field type. Note that the [name=""] attribute
	 * in the @Child annotation needs to match the name for the bean accessor
	 * and mutator methods.
	 */
	@Child(name="petName")	
	@Extension(url="http://example.com/dontuse#petname", definedLocally=false, isModifier=false)
	@Description(shortDefinition="The name of the patient's favourite pet")
	private StringType myPetName;

	/**
	 * The second example extension uses a List type to provide
	 * repeatable values. Note that a [max=] value has been placed in
	 * the @Child annotation.
	 * 
	 * Note also that this extension is a modifier extension
	 */
	@Child(name="importantDates", max=Child.MAX_UNLIMITED)	
	@Extension(url="http://example.com/dontuse#importantDates", definedLocally=false, isModifier=true)
	@Description(shortDefinition="Some dates of note for this patient")
	private List<DateTimeType> myImportantDates;

	/**
	 * It is important to override the isEmpty() method, adding a check for any
	 * newly added fields. 
	 */
	@Override
	public boolean isEmpty() {
		return super.isEmpty() && ElementUtil.isEmpty(myPetName, myImportantDates);
	}
	
	/********
	 * Accessors and mutators follow
	 * 
	 * IMPORTANT:
	 * Each extension is required to have an getter/accessor and a stter/mutator. 
	 * You are highly recommended to create getters which create instances if they
	 * do not already exist, since this is how the rest of the HAPI FHIR API works. 
	 ********/
	
	/** Getter for important dates */
	public List<DateTimeType> getImportantDates() {
		if (myImportantDates==null) {
			myImportantDates = new ArrayList<DateTimeType>();
		}
		return myImportantDates;
	}

	/** Getter for pet name */
	public StringType getPetName() {
		if (myPetName == null) {
			myPetName = new StringType();
		}
		return myPetName;
	}

	/** Setter for important dates */
	public void setImportantDates(List<DateTimeType> theImportantDates) {
		myImportantDates = theImportantDates;
	}

	/** Setter for pet name */
	public void setPetName(StringType thePetName) {
		myPetName = thePetName;
	}

}

Using this custom type is as simple as instantiating the type and working with the new fields.

MyPatient patient = new MyPatient();
patient.setPetName(new StringDt("Fido"));
patient.getImportantDates().add(new DateTimeDt("2010-01-02"));
patient.getImportantDates().add(new DateTimeDt("2014-01-26T11:11:11"));

patient.addName().addFamily("Smith").addGiven("John").addGiven("Quincy").addSuffix("Jr");

IParser p = FhirContext.forDstu2().newXmlParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(patient);

System.out.println(messageString);

This example produces the following output:

<Patient xmlns="http://hl7.org/fhir">
   <modifierExtension url="http://example.com/dontuse#importantDates">
      <valueDateTime value="2010-01-02"/>
   </modifierExtension>
   <modifierExtension url="http://example.com/dontuse#importantDates">
      <valueDateTime value="2014-01-26T11:11:11"/>
   </modifierExtension>
   <extension url="http://example.com/dontuse#petname">
      <valueString value="Fido"/>
   </extension>
   <name>
      <family value="Smith"/>
      <given value="John"/>
      <given value="Quincy"/>
      <suffix value="Jr"/>
   </name>
</Patient>

Parsing messages using your new custom type is equally simple. These types can also be used as method return types in clients and servers.

IParser parser = FhirContext.forDstu2().newXmlParser();
MyPatient newPatient = parser.parseResource(MyPatient.class, messageString);

Using Custom Types in a Client

If you are using a client and wish to use a specific custom structure, you may simply use the custom structure as you would a build in HAPI type.

// Create an example patient
MyPatient custPatient = new MyPatient();
custPatient.addName().setFamily("Smith").addGiven("John");
custPatient.setPetName(new StringType("Rover")); // populate the extension

// Create the resource like normal
client.create().resource(custPatient).execute();

// You can also read the resource back like normal
custPatient = client.read().resource(MyPatient.class).withId("123").execute();

You may also explicitly use custom types in searches and other operations which return resources.

// Perform the search using the custom type
Bundle bundle = client
   .search()
   .forResource(MyPatient.class)
   .returnBundle(Bundle.class)
   .execute();

// Entries in the return bundle will use the given type
MyPatient pat0 = (MyPatient) bundle.getEntry().get(0).getResource();

You can also explicitly declare a preferred response resource custom type. This is useful for some operations that do not otherwise declare their resource types in the method signature.

//Perform the search using the custom type
bundle = client
   .history()
   .onInstance(new IdType("Patient/123"))
   .andReturnBundle(Bundle.class)
   .preferResponseType(MyPatient.class)
   .execute();

//Entries in the return bundle will use the given type
MyPatient historyPatient0 = (MyPatient) bundle.getEntry().get(0).getResource();

Using Multiple Custom Types in a Client

Sometimes you may not know in advance exactly which type you will be receiving. For example, there are Patient resources which conform to several different profiles on a server and you aren't sure which profile you will get back for a specific read, you can declare the "primary" type for a given profile.

This is declared at the FhirContext level, and will apply to any clients created from this context (including clients created before the default was set).

FhirContext ctx = FhirContext.forDstu3();

// Instruct the context that if it receives a resource which
// claims to conform to the given profile (by URL), it should
// use the MyPatient type to parse this resource
ctx.setDefaultTypeForProfile("http://example.com/StructureDefinition/mypatient", MyPatient.class);

// You can declare as many default types as you like
ctx.setDefaultTypeForProfile("http://foo.com/anotherProfile", CustomObservation.class);

// Create a client
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu3");

// You can also read the resource back like normal
Patient patient = client.read().resource(Patient.class).withId("123").execute();
if (patient instanceof MyPatient) {
   // If the server supplied a resource which declared to conform
   // to the given profile, MyPatient will have been returned so
   // process it differently..
}

Using Custom Types in a Server

If you are using a client and wish to use a specific custom structure, you may simply use the custom structure as you would a build in HAPI type.

// Create an example patient
MyPatient custPatient = new MyPatient();
custPatient.addName().setFamily("Smith").addGiven("John");
custPatient.setPetName(new StringType("Rover")); // populate the extension

// Create the resource like normal
client.create().resource(custPatient).execute();

// You can also read the resource back like normal
custPatient = client.read().resource(MyPatient.class).withId("123").execute();

Custom Type Examples: Composite Extensions

The following example shows a resource containing a composite extension.

@ResourceDef(name = "Patient")
public class CustomCompositeExtension extends Patient {

	private static final long serialVersionUID = 1L;

	/**
	 * A custom extension
	 */
	@Child(name = "foo")
	@Extension(url="http://acme.org/fooParent", definedLocally = false, isModifier = false)
	protected FooParentExtension fooParentExtension;

	public FooParentExtension getFooParentExtension() {
		return fooParentExtension;
	}

	@Override
	public boolean isEmpty() {
		return super.isEmpty() && ElementUtil.isEmpty(fooParentExtension);
	}

	public void setFooParentExtension(FooParentExtension theFooParentExtension) {
		fooParentExtension = theFooParentExtension;
	}

	@Block
	public static class FooParentExtension extends BackboneElement {

		private static final long serialVersionUID = 4522090347756045145L;

		@Child(name = "childA")
		@Extension(url = "http://acme.org/fooChildA", definedLocally = false, isModifier = false)
		private StringType myChildA;

		@Child(name = "childB")
		@Extension(url = "http://acme.org/fooChildB", definedLocally = false, isModifier = false)
		private StringType myChildB;

		@Override
		public FooParentExtension copy() {
			FooParentExtension copy = new FooParentExtension();
			copy.myChildA = myChildA;
			copy.myChildB = myChildB;
			return copy;
		}

		@Override
		public boolean isEmpty() {
			return super.isEmpty() && ElementUtil.isEmpty(myChildA, myChildB);
		}

		public StringType getChildA() {
			return myChildA;
		}

		public StringType getChildB() {
			return myChildB;
		}

		public void setChildA(StringType theChildA) {
			myChildA = theChildA;
		}

		public void setChildB(StringType theChildB) {
			myChildB = theChildB;
		}

	}

}

This could be used to create a resource such as the following:

<Patient xmlns="http://hl7.org/fhir">
   <id value="123"/>
   <extension url="http://acme.org/fooParent">
      <extension url="http://acme.org/fooChildA">
         <valueString value="ValueA"/>
      </extension>
      <extension url="http://acme.org/fooChildB">
         <valueString value="ValueB"/>
      </extension>
   </extension>
</Patient>

Back to top

Reflow Maven skin by Andrius Velykis.