jsonjsonschema

How to make anyOf a set of mutually exclusive properties except one


I have a legacy API I'm trying to define in a JSON Schema and the object has a weird structure where there are a set of 4 properties, any one of them is required, and 3 of them are mutually exclusive. The are a more than 30 shared optional properties after that as well, I'll note them as ....

e.g.,

{ "foo": "bar", "baz": 1234, ... }  // OK
{ "foo": "bar", "buzz": 1234, ... } // OK
{ "foo": "bar", "fizz": 1234, ... } // OK
{ "foo": 1234, ... }                // OK
{ "baz": 1234, ... }                // OK
{ ... }                             // NOT OK
{ "baz": 1234, "buzz": 1234, ... }  // NOT OK

I could do a oneOf but that doesn't allow foo to be present with the others, anyOf allows for baz,buzz, and fizz to be present with each other which is not possible.

I tried to define something like the following:

{
    "type": "object",
    "properties": {
        "foo": {"type": "string"},
        "baz": {"type": "number"},
        "buzz": {"type": "number"},
        "fizz": {"type": "number"}
    },
    "anyOf": [
        {"required": ["foo"]},
        {"required": [{"oneOf": [
                {"required": ["baz"]},
                {"required": ["buzz"]},
                {"required": ["fizz"]}
            ]}
        ]}            
    ]
}

and

{
    "type": "object",
    "properties": {
        "foo": {"type": "string"},
        "baz": {"type": "number"},
        "buzz": {"type": "number"},
        "fizz": {"type": "number"}
    },
    "anyOf": [
        {"required": ["foo"]},
        {"oneOf": [
                {"required": ["baz"]},
                {"required": ["buzz"]},
                {"required": ["fizz"]}
            ]
        }            
    ]
}

But that does not work and I just don't know enough about json schema yet to know if this possible.


Solution

  • You can make properties mutually exclusive in JSON Schema using pairwise exclusions, but this leads to combinatorial explosion. That becomes a problem when you have many mutually exclusive properties.

    A linear solution is of the form:

    This only pays off if you have many properties.

    { "oneOf": [
      { "required": ["baz"] },
      { "required": ["buzz"] },
      { "required": ["fizz"] },
      { "not":
        { "anyOf": [
          { "required": ["baz"] },
          { "required": ["buzz"] },
          { "required": ["fizz"] }
        ] }
      }
    ] }
    

    Combine this with @cloudfeet's answer to get the answer to your specific question.