1

On the face of it, this seems like a duplicate of this other Q&A, but in practical terms it is not.

I have a schema where I have an object that contains approximately a dozen different properties, but any given use of that object is only permitted to have exactly one of the properties. I have tried using a "oneOf" sequence of "required" definitions like:

...
"myObject": {
  "type": "object",
  "oneOf": [
    {
      "required": [
        "a"
      ]
    },
    {
      "required": [
        "b"
      ]
    },
    {
      "required": [
        "c"
      ]
    },
    ...
  ],
  "properties": {
    "a": {...},
    "b": {...},
    "c": {...},
    ...
  }
}
...

However, when this schema is applied, using either IntelliJ IDEA and VSCode, I can still define a "myObject" that contains multiple properties, such as:

"myObject": {
  "a": ...,
  "b": ...
}

How can I define the schema to allow just one of these properties?

In addition, I have an extended version of this structure like:

...
"myExtendedObject": {
  "type": "object",
  "required": [
    "op",
    "val"
  ],
  "allOf": [
    {
      "$ref": "#/definitions/myObject"
    },
    {
      "properties": {
        "op": {...},
        "val": {...}
      }
    }
  ]
}
...

In this scenario I want the schema to require exactly one of the first dozen properties "a", "b", "c", or ..., plus "op" and "val". Again, how to achieve this?

In general terms to solve this "one and only one of these properties" validation rule, do I need to do something as verbose and horrid as:

"myObject": {
  "type": "object",
  "oneOf": [
    {
      "required": [
        "a"
      ],
      "not": {
        "required": [
          "b",
          "c",
          ...
        ]
      }
    },
    {
      "required": [
        "b"
      ],
      "not": {
        "required": [
          "a",
          "c",
          ...
        ]
      }
    },
    {
      "required": [
        "c"
      ],
      "not": {
        "required": [
          "a",
          "b",
          ...
        ]
      }
    },
    ...

I would very much like to avoid this latter since it is a huge amount of additional repetition with subtle variation.

PS: Yes, I am aware there are many newer schema versions but I'm stuck on draft 7 since that's what the schema aware editors I have available can understand.

UPDATE:

  1. I am already using "additionalProperties": false in my schema and note that this includes lots of hoop jumping in order to support the extension scenarios that use an allOf[{"$ref": "#/definitions/myObject"},{...}] approach. I omitted this to reduce the size of the examples I provided.

  2. I have already tried maxProperties (which has to be done in a way that is compatible with the need to support extension), and while this helps flag error cases the actual error doesn't actually tell you what you shouldn't have put in the JSON, just that there are too many properties.

UPDATE 2:

After a nudge by @JasonDesrosiers, where he indirectly pointed out that De Morgan's law applies here (i.e. not(A AND B AND ...) == not(A) OR not(B) OR ...) I tried a slightly different, even more verbose, approach of:

"myObject": {
  "type": "object",
  "oneOf": [
    {
      "required": [
        "a"
      ],
      "not": {
        "anyOf": [
          {"required": ["b"]},
          {"required": ["c"]},
          ...
        ]
      }
    },
    ...

While this resolved the validation, the feedback to the user isn't great (and actually can be a bit confusing) this didn't impact the editor auto-completion behaviour in the way I hoped (or, indeed, at all).

Phil W
  • 123
  • 1
  • 6
  • Your original schema with `oneOf`/`required` is definitely the correct JSON Schema solution. If it doesn't work in your IDE/editor, that's a bug or a limitation of the IDE/editor. You'll need a vendor specific workaround or a bugfix/enhancement from the IDE/editor. – Jason Desrosiers Aug 25 '21 at 17:59
  • Also, checkout https://stackoverflow.com/questions/61059683/how-to-conditionally-forbid-properties-based-on-presence-of-other-properties-in/61062869#61062869 to see why `not`/`required` in your last example doesn't work the way you might think it does. – Jason Desrosiers Aug 25 '21 at 18:01
  • @JasonDesrosiers, a-ha! So, the main issue is that "not-required" application to multiple property names directly, instead of an anyOf sequence of single property "not-required"?! I'll give it a go. Note that I get the same behaviour from IntelliJ IDEA and VSCode JSON schema aware editors. Either they both use the same engine or there's a common misunderstanding of draft 7?! – Phil W Aug 25 '21 at 18:22
  • @JasonDesrosiers, thanks again. See "UPDATE 2" in the question, above. Even without using "dependencies" keyword the validation is certainly better, if not perfect. I wonder how the latter keyword would affect it? I'll try it if/when I find the time. – Phil W Aug 25 '21 at 19:12
  • 1
    Yes, IDEA and VSCode have the same bug/limitation. Try on https://jsonschema.dev/s/wxz4F, https://www.jsonschemavalidator.net/s/0pwrflcr, and https://json-schema.hyperjump.io. All three use different implementations and all show proper validation behavior. You should not need the `not` at all. The only thing `not` does is complicate the error response. The only reason to include it is if it's necessary to work around the bug. – Jason Desrosiers Aug 25 '21 at 19:45
  • Adding the "not" piece has borked IDEA - analysis seems to spin out of control consuming at least 10.5Gb heap, freezing the UI and seemingly never finishing... I guess I'll revert to the "maxProperties" for now. – Phil W Aug 25 '21 at 19:53

2 Answers2

1

You can do that with: "maxProperties": 1

You can also, instead of saying not: required: [ ... ] in your if/then/else clauses, just add "additionalProperties": false.

Ether
  • 53,118
  • 13
  • 86
  • 159
  • Hi. I already use the additionalProperties stuff (which is more long winded when you need to do the extension like I have) and tried maxProperties but that is also less than ideal where I need to do the extension. For the latter I tried maxProperties as 3 but this does not lead to good auto-completion behaviours in the schema-aware editors. – Phil W Aug 25 '21 at 06:04
  • I have updated the question to clarify these points. – Phil W Aug 25 '21 at 10:57
0

Given your constraints:

  • you can't move to a newer draft
  • you don't like the other suggested answer (from Ether)
  • you are concerned about the use of the schema in editors for autocomplete

Does the verbose approach work?

Use in editors for auto complete is non-standard, so each editor may have a different approach.

If the verbose approach does work, consider using a pre-processor to build your schema, either rolling your own or https://jsonnet.org to take the repition away.

Otherwise, ask the editors if they provide an alternative approach, and maybe file an issue? It's difficult when JSON Schema is used for things it wasn't designed for.

Relequestual
  • 11,631
  • 6
  • 47
  • 83
  • Hello again! Thanks for contributing. Just to clarify; it's not that I don't like the existing suggestion, but rather that this doesn't work well when you need to allow for "extension" (as I have mentioned and as you helped me out with last year). Interestingly auto-completion in IDEA and VSCode seem to work in very similar ways. The other issue I have is that using `maxProperties` doesn't provide good feedback to the JSON author about what it is they have added that they should not, again especially in the "extended object" context. – Phil W Aug 25 '21 at 16:38
  • I tried my verbose approach, but this actually seemed not to work. I even split the top level object definition into a `oneOf` sequence where each had one property and one required property, but that didn't fly either. As a sticking plaster I've added `maxProperties`, but longer term I want to remove this and have that clean auto-completion suggestion list. More fiddling around clearly needed! – Phil W Aug 25 '21 at 16:45