yesdnil5
yesdnil5

Reputation: 1

JSON Schema - One or none of multiple properties. Easier way?

I'm working with the hl7 FHIR json schemas and there are pretty simple and lacking in some logic that I'd rather not write code for. There are properties that are mutually exclusive but are not required and I'm trying to find the best way to represent that in the json schema.

Let's take this one for example, specifically this part:

"onsetDateTime": {
  "description": "Estimated or actual date or date-time  the condition 
  began, in the opinion of the clinician.",
  "pattern": "-?[0-9]{4}(-(0[1-9]|1[0-2])(-(0[0-9]|[1-2][0-9]|3[0-1])
  (T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\\.[0-9]+)?(Z|(\\+|-)
  ((0[0-9]|1[0-3]):[0-5][0-9]|14:00)))?)?)?",
  "type": "string"
},
"_onsetDateTime": {
  "description": "Extensions for onsetDateTime",
  "$ref": "Element.schema.json#/definitions/Element"
},
"onsetAge": {
  "description": "Estimated or actual date or date-time  the condition 
  began, in the opinion of the clinician.",
  "$ref": "Age.schema.json#/definitions/Age"
},
"onsetPeriod": {
  "description": "Estimated or actual date or date-time  the condition 
  began, in the opinion of the clinician.",
  "$ref": "Period.schema.json#/definitions/Period"
},
"onsetRange": {
  "description": "Estimated or actual date or date-time  the condition 
  began, in the opinion of the clinician.",
  "$ref": "Range.schema.json#/definitions/Range"
},
"onsetString": {
  "description": "Estimated or actual date or date-time  the condition 
  began, in the opinion of the clinician.",
  "type": "string"
},
"_onsetString": {
  "description": "Extensions for onsetString",
  "$ref": "Element.schema.json#/definitions/Element"
}

I was able to get it done but it's tedious and long. I don't mind doing it, but I was wondering if there was a simpler way.

Here's what I got:

"dependencies":
  "onsetDateTime": {
    "allOf": [
      {
        "not": {"required": ["onsetAge"]}
      },
      {
        "not": {"required": ["onsetPeriod"]}
      },
      {
        "not": {"required": ["onsetRange"]}
      },
      {
        "not": {"required": ["onsetString"]}
      },
      {
        "not": {"required": ["_onsetString"]}
      }
    ]
  },
  "onsetAge": {
    "allOf": [
      {
        "not": {"required": ["_onsetDateTime"]}
      },
      {
        "not": {"required": ["onsetPeriod"]}
      },
      {
        "not": {"required": ["onsetRange"]}
      },
      {
        "not": {"required": ["onsetString"]}
      },
      {
        "not": {"required": ["_onsetString"]}
      }
    ]
  },
  "onsetPeriod": {
    "allOf": [
      {
        "not": {"required": ["_onsetDateTime"]}
      },
      {
        "not": {"required": ["onsetRange"]}
      },
      {
        "not": {"required": ["onsetString"]}
      },
      {
        "not": {"required": ["_onsetString"]}
      }
    ]
  },
  "onsetRange": {
    "allOf": [
      {
        "not": {"required": ["_onsetDateTime"]}
      },
      {
        "not": {"required": ["onsetString"]}
      },
      {
        "not": {"required": ["_onsetString"]}
      }
    ]
  },
  "onsetString": {
    "allOf": [
      {
        "not": {"required": ["_onsetDateTime"]}
      }
    ]
  }
}

Any ideas/solutions? There's another one like this in the same json schema so the dependencies are pretty long and the other schemas have more that just 5 properties to choose from.

Thanks!

Upvotes: 0

Views: 2627

Answers (2)

erosb
erosb

Reputation: 3141

You can try using the "dependencies", "not" and "required" to solve this. So for example, to express that "if onsetAge is present then onsetPeriod and onsetRange should not be present" you can do the following:

{
  "dependencies": {
    "onsetAge": {
      "allOf": [
        {"not": {"required": ["onsetPeriod"]}},
        {"not": {"required": ["onsetRange"]}}
      ]
    }
  }
}

(I didn't test it, just an idea)

Upvotes: 0

AbigailW
AbigailW

Reputation: 893

Glad you're finding the FHIR Json Schemas to be of use!

So, a couple things... the Json Schema that you're pointing to aren't the most up-to-date. They're on the official site as part of STU3; but the most recent version is from the Jan 2018 Working Group, and has a bunch of updates and fixes, and uses a new all-in-one format to resolve circular dependencies and other $ref issues. Get the latest at either of the following links.

http://build.fhir.org/fhir.schema.json.zip
https://www.npmjs.com/package/fhir-schemas

You are right though... there's still room for improvement. And, happily, it's an active area of development right now, and these kinds of improvements are have a good chance of getting incorporated into the official schemas.

So, taking a look at the problem you propose, we may be able to get some better milage out of oneOf and possibly anyOf. Based on the most recent version of the Condition resource, I'd try something like the following:

{
  "Condition": {
    "description": "A clinical condition, problem, diagnosis...",
    "properties": {
      "resourceType": {
          "const": "Condition"
      },
      "id": {...},
      "meta": {...},
      "oneOf": [
        "onsetDateTime": {...},
        "_onsetDateTime": {...},
        "onsetAge": {...},
        "onsetPeriod": {...},
        "onsetRange": {...},
        "onsetString": {...},
        "_onsetString": {...}
      ]
    }
  }
}

It may be as simple as wrapping the onset* fields in an oneOf array.

Upvotes: 1

Related Questions