bilcker
bilcker

Reputation: 1130

Ensure only one property of an array is true in a JSON schema

I have an array of options. Each item in the array will have text and a boolean value isAnswer I am trying to validate in a way that only one of the items can be and must be marked true. Anything else should be invalid. Via two items are true or 0 are true should fail. I have been playing around with oneOf as that seems to make the most sense however it always validates successful.

First off, is this possible to validate? Secondly am I on the right track?

Thanks for any help you can offer

   "question": {
      "title": "Question",
      "type": "object",
      "properties": {
        "options": {
          "title": "Options",
          "type": "array",
          "minItems": 2,
          "maxItems": 10,
          "items": {
            "title": "Option",
            "type": "object",
            "properties": {
              "isAnswer": {
                "title": "Answer",
                "type": "boolean",
                "format": "checkbox",
                "default": false
              },
              "text": {
                "title": "Choice Text",
                "type": "string"
              },
            },
            "oneOf": [
              {
                "properties": {
                  "isAnswer": true
                }
              }
            ]
          }
        }
      }
    }

Upvotes: 0

Views: 1414

Answers (2)

awwright
awwright

Reputation: 645

This kind of test is called "business logic" or "data consistency validation" which falls outside the scope of JSON Schema (and most validation tools, such as XML Schema, RELAX NG, and the like). See Scope of JSON Schema Validation for more information on this.

It may be technically possible to write a schema that produces the results you're looking for, by crafting different schemas, one for each possible correct answer. Similar solutions are used to validate different classes of objects, where a "category" or "type" field determines how to validate all the other properties.

However, JSON Schema does not support comparing one value with another in the general.

As far as the schema layout goes, your schema is acceptable; but I would consider specifying the answer separately from the questions:

{
  "options": [
    { "text": "Butaful" },
    { "text": "Bueatful" },
    { "text": "Beautiful" },
    { "text": "Beeyoutiful" }
  ],
  "answer": "Beautiful"
}

This adds some redundancy, and supports freeform answers.

Upvotes: 1

Ethan
Ethan

Reputation: 725

this question is almost identical to How to enforce only one property value to true in an array (JSON Schema) - check the answer to that one.

this is a little bit different because you have a maxItems - this opens up an ugly option of brute-forcing the possible combinations.

I'm going to pretend your maxItems is 3 instead of 10 to cut down on verbosity:

definitions:
  correctAnswer:
    {properties: {isAnwser: {const: true}}}
  incorrectAnswer:
    {properties: {isAnwser: {const: false}}}

oneOf:
  - items: [{'$ref': '#/definitions/correctAnswer'}, {'$ref': '#/definitions/incorrectAnswer'}, {'$ref': '#/definitions/incorrectAnswer'}]
  - items: [{'$ref': '#/definitions/incorrectAnswer'}, {'$ref': '#/definitions/correctAnswer'}, {'$ref': '#/definitions/incorrectAnswer'}]
  - items: [{'$ref': '#/definitions/incorrectAnswer'}, {'$ref': '#/definitions/incorrectAnswer'}, {'$ref': '#/definitions/correctAnswer'}]

ugly and unmaintainable! better to write this requirement in your code, unless / until you can use 2019-09.

other notes:

  • oneOf checks that one of a set of schemas validates against the instance, not whether one element of an array instance validates against a schema.

  • you have "properties": {"isAnswer": true} - what you want there is "properties": {"isAnswer": {"const": true}}. what you have will use the true schema which matches any instance. const matches an instance equal to its value.

Upvotes: 2

Related Questions