Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mutually exclusive property groups

Tags:

jsonschema

Suppose I have an object with four possible properties: a, b, c, d. a and b can only appear together (i.e., a appears if and only if b appears). If a and b appear, c cannot appear (that is, a/b and c are mutually exclusive). If a and b do not appear, c may appear (but is not required to). d can appear in any combination with a/b, c, or on its own. No properties other than a, b, c, or d may appear at all.

How do I express this as a jsonschema? I suspect I could use some combination of oneOf and required, but I can't figure out the proper incantation.

like image 586
Ray Avatar asked Jan 27 '15 02:01

Ray


2 Answers

You can phrase your constraints as:

  • either: both "a" and "b" are present, and "c" is not present
  • or: neither "a" nor "b" is present. ("c" may or may not be present)

Saying "neither" in the second point is a bit verbose. Here, we've expressed it using allOf/not. (Note: you can't factor them into a single required clause here, because you need a separate not for each one.)

{
    "oneOf": [
        {
            "required": ["a", "b"],
            "not": {"required": ["c"]}
        },
        {
            "allOf": [
                {
                    "not": {"required": ["a"]}
                },
                {
                    "not": {"required": ["b"]}
                }
            ]
        }
    ]
}

Alternative structure

There's also another way to say "neither", which is actually to use oneOf again. Since you must pass exactly one of a oneOf clause, if one of the entries is {} (passes everything), then all the other options are banned.

While it's slightly more concise, it's possibly slightly less intuitive to read:

{
    "oneOf": [
        {
            "required": ["a", "b"],
            "not": {"required": ["c"]}
        },
        {
            "oneOf": [
                {},
                {"required": ["a"]},
                {"required": ["b"]}
            ]
        }
    ]
}
like image 113
cloudfeet Avatar answered Jan 01 '23 00:01

cloudfeet


Another alternative is to use the schema dependencies declaration:

   "dependencies": {
     "c": {
       "allOf": [
       { 
         "not": { "required": [ "a" ] }
       },
       { 
         "not": { "required": [ "b" ] }
       }
     ]
     }, 
     "a": {
       "allOf": [
       { 
         "not": { "required": [ "c" ] }
       },
       { 
         "required": [ "b" ]
       }
     ]
     },
     "b": {
       "allOf": [
       { 
         "not": { "required": [ "c" ] }
       },
       { 
         "required": [ "a" ]
       }
     ]
     }
   }
like image 27
JonasL Avatar answered Dec 31 '22 23:12

JonasL