In a prior post I proposed an alternate XML syntax for FHIR extensions that is based on the use of XML namespaces. JSON doesn’t natively support namespaces so how can this work?
Here’s an example of the current JSON syntax for FHIR extensions:
{ "resourceType" : "Patient", "extension" : [{ "url" : "http://hl7.org/fhir/StructureDefinition/patient-clinicalTrial", "extension" : [{ "url" : "NCT", "valueString" : "123456789" }, { "url" : "period", "valuePeriod" : { "start" : "2009-03-14" } }, { "url" : "reason", "valueCodeableConcept" : { "coding" : { "system" : "http://acme.org/codes/general", "code" : "tt14j" } } }] }] ... }
Here’s the proposed alternative:
{ "@context" : { "patient-clinicalTrial": { "@id": "http://hl7.org/fhir/StructureDefinition/patient-clinicalTrial", "@isModifier": false } }, "resourceType" : "Patient", "patient-clinicalTrial" : { "NCT" : { "valueString" : "123456789" }, "period": { "valuePeriod" : { "start" : "2009-03-14" } }, "reason": { "valueCodeableConcept" : { "coding" : { "system" : "http://acme.org/codes/general", "code" : "tt14j" } } } } ... }
The proposed alternative copies the idea of a @context
property from JSON-LD though note that I’m not suggesting to use JSON-LD wholesale, I’ll leave that to the folks working on the RDF representation of FHIR. Here the @context
is used to provide the unique StructureDefinition
identifier of the patient-clinicalTrial
extension property and define whether this extension modifies the semantics of the parent. The properties of the clinicalTrial
extension are identified by the name
defined in the StructureDefinition
.
Notice how much more naturally you can now address extension property values as simple paths into the object:
patient = {...second example above...} nct = patient["patient-clinicalTrial"].NCT.valueString
Compare this to the process needed to extract the NCT from the current representation:
- Loop over all of the extensions
- Find extensions with a
url
value ofhttp://hl7.org/fhir/StructureDefinition/patient-clinicalTrial
- Loop over all of the child extensions of these extensions
- Find child extensions with a
url
value ofNCT
- Extract the
valueString
property value of the child extension
To illustrate further, here’s some sample code (CoffeeScript) to extract the NCT from the current representation:
patient = {...first example above...} clinicalTrials = (extension for extension in patient.extension when extension.url is "http://hl7.org/fhir/StructureDefinition/patient-clinicalTrial") ncts = (extension for extension in clinicalTrials[0].extension when extension.url is "NCT") nct = ncts[0].valueString
One of the benefits here is the elimination of arrays for single-valued extensions as show above but what about extensions with multiple values? You can’t use the same property name more than once for a given object so we’d need to resort back to arrays like this:
{ "@context" : { "patient-clinicalTrial": { "@id": "http://hl7.org/fhir/StructureDefinition/patient-clinicalTrial", "@isModifier": false } }, "resourceType" : "Patient", "patient-clinicalTrial" : [{ "NCT" : { "valueString" : "123456789" }, "period": { "valuePeriod" : { "start" : "2009-03-14" } }, "reason": { "valueCodeableConcept" : { "coding" : { "system" : "http://acme.org/codes/general", "code" : "tt14j" } } } }, { "NCT" : { "valueString" : "987654321" }, "period": { "valuePeriod" : { "start" : "2011-07-12" } }, "reason": { "valueCodeableConcept" : { "coding" : { "system" : "http://acme.org/codes/general", "code" : "ts15p" } } } }] ... }
Paths into the object are still nice and clear:
patient["patient-clinicalTrial"][0].NCT.valueString patient["patient-clinicalTrial"][1].NCT.valueString
How about multiple extensions? Just add additional @context
entries for each extension:
{ "@context" : { "patient-clinicalTrial": { "@id": "http://hl7.org/fhir/StructureDefinition/patient-clinicalTrial", "@isModifier": false } "race": { "@id": "http://hl7.org/fhir/StructureDefinition/us-core-race", "@isModifier": false } }, "resourceType": "Patient", "patient-clinicalTrial": { ... }, "race": {...} ... }
Finally, how does this approach line up with the requirements outlined earlier?
- Supports distributed extensibility: Yes, anybody can define an extension in their own URI-defined namespace
- Explicitly identifies modifying extensions: Yes via the
@isModifier
property in the@context
- Includes extension URI: Yes, as the value of the
@id
property - Parsing without reference to definition: Yes
- Links to StructureDefinition: Yes, via the
@isModifier
property in the@context
- Improved readability: Yes, use local names for elements rather than everything being an extension
- Leverages existing standards: Partial use of JSON-LD
- Leverages validation standards: Could be described by JSON Schema
- Treats extensions and core elements equivalently: Yes, with the exception that extensions must be defined in a
@context