An Alternate JSON Syntax for FHIR Extensions

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 of http://hl7.org/fhir/StructureDefinition/patient-clinicalTrial
  • Loop over all of the child extensions of these extensions
  • Find child extensions with a url value of NCT
  • 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?

  1. Supports distributed extensibility: Yes, anybody can define an extension in their own URI-defined namespace
  2. Explicitly identifies modifying extensions: Yes via the @isModifier property in the @context
  3. Includes extension URI: Yes, as the value of the @id property
  4. Parsing without reference to definition: Yes
  5. Links to StructureDefinition: Yes, via the @isModifier property in the @context
  6. Improved readability: Yes, use local names for elements rather than everything being an extension
  7. Leverages existing standards: Partial use of JSON-LD
  8. Leverages validation standards: Could be described by JSON Schema
  9. Treats extensions and core elements equivalently: Yes, with the exception that extensions must be defined in a @context

About Marc Hadley

Software engineer at the MITRE Corporation
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s