0

update: added requirements

I am trying to implement a JSON schema that works like so…

given the following params: foo, bar, baz, one, and two

One of the following should apply

  • either no param exists
  • if one or more params exist then
    • if foo exists - then one and two are not required
    • else if any param(s) other than foo exist(s) - then one and two are required

of course, if any params are provided that are not in the schema then an appropriate error should be raised.

After a lot of finagling, and inspiration, here is my attempt which doesn't really work

{
    "type": "object",
    "properties": {
        "foo": { "type": "string" },
        "bar": { "type": "string" },
        "baz": { "type": "string" }
    },
    "oneOf": [
        {
            "not": {
                "properties": { "foo": { "const": "" } },
                "required": []
            }
        },
        { "required": [ "one", "two" ] }
    ]
}

I also tried the following but was not successful

{
    "type": "object",
    "properties": {
        "foo": { "type": "string" },
        "bar": { "type": "string" },
        "baz": { "type": "string" }
    },
    "dependencies": {
        "not": {
            "foo": { "required": ["one", "two"] }
        }
    }
}
punkish
  • 13,598
  • 26
  • 66
  • 101

1 Answers1

1

I suspect there might be a hidden requirement or two in here, but here's a solution based on your requirements. Let me know if there's something I'm missing and I'll update my answer.

Most of your requirements are the default behavior in JSON Schema, so you don't have to do anything.

  • either no param exists

All properties are optional by default, so nothing to do here

  • if foo exists - then one and two are not required

Again, optional is the default, so nothing to do here

  • if one or more params exist then [and] any param(s) other than foo exist(s) - then one and two are required

The best way to express this is with if/then. If "foo" is optional, it's a little complicated to express, "any param(s) other than foo", so I put that in definitions to make it a bit easier to read.

if any params are provided that are not in the schema then an appropriate error should be raised.

The additionalProperties keyword takes care of this. You just need to add "one" and "two".

{
  "type": "object",
  "properties": {
    "foo": { "type": "string" },
    "bar": { "type": "string" },
    "baz": { "type": "string" },
    "one": {},
    "two": {}
  },
  "additionalProperties": false,
  "if": { "$ref": "#/definitions/has-property-other-than-foo" },
  "then": { "required": ["one", "two"] },
  "definitions": {
    "has-property-other-than-foo": {
      "if": { "required": ["foo"] },
      "then": { "minProperties": 2 },
      "else": { "minProperties": 1 }
    }
  }
}
Jason Desrosiers
  • 22,479
  • 5
  • 47
  • 53
  • I accepted your answer, and then unaccepted it because I realized one (inconvenient) issue with it. For the ease of discussion, let's call **foo** the Primary Key. If PK does not exist, then the `"anyOf"` condition lists every non-PK property on a separate line, for example, `{ "required": ["bar"] }`. My schema has many (25-30) such non-PK properties. Is there a way to say "everything other than 'foo'" in this `"anyOf"` clause? – punkish Sep 29 '20 at 06:05
  • yes, it works. So many thanks. I have marked your answer as accepted. If I can ask you one more favor. Could you kindly explain what the heck is going on in the `"has-property-other-than-foo"` block? I am trying to convert into plain English, but by my logic, "if required 'foo', then minProperties should be 1" (that is, nothing else should be allowed). Or, it could mean, "if 'foo' is present then the number of "required" properties should be 2 otherwise they should be 0". But neither of those seem to be implied by the "has-property-other-than-foo" block. It does work, and for that, kudos !! – punkish Sep 30 '20 at 01:04
  • If the object has a property called "foo" and there are at least 2 properties, it means that it must have a property other than "foo". If the object doesn't have a property called "foo" and there is at least 1 property, it means that it must have a property other than "foo". Hope that helps. – Jason Desrosiers Oct 01 '20 at 00:32