user8479984
user8479984

Reputation: 501

Nesting oneOf in anyOf for a JSON Schema

Here is the JSON Schema and the JSON as provided below in the link for illustartion purpose.

JSON Schema and the JSON

Format: The Individual JSON object (with their additional attributes and may vary with other object in the array) within array can be of any 3 region: 'america', 'asia' and 'europe' and at-least on the type of region object should be there. This can be achieved by array minItems property)

Problem Statement:

  1. The Individual JSON object within array can be of any 3 region: 'america', 'asia' and 'europe' and at-least on the type of region object should be there

    ==> I am able to solve this by putting all the region objects in the anyOf array as I want to match at-least one of the valid region object.

  2. Either the JSON object 'asia' or 'europe' can exist along with other region type. Both cannot coexist.

    ==> I tried to use 'oneOf' but its passing the ajv validation. Actually it should fail. Can anyone help. Thanks

JSON Schema

{
    "type": "object",
    "properties": {
        "stat_data": {
            "type": "array",
            "minItems": 1,
            "items": {
                "type": "object",
                "properties": {},
                "anyOf": [{
                        "required": ["region"],
                        "properties": {
                            "region": {
                                "enum": ["america"]
                            },
                            "country": {
                                "type": "string"
                            },
                            "population": {
                                "type": "string"
                            }
                        }
                    },
                    {
                        "oneOf": [
                            {
                                "required": ["region"],
                                "properties": {
                                    "region": {
                                        "enum": ["asia"]
                                    },
                                    "country": {
                                        "type": "string"
                                    },
                                    "details": {
                                        "type": "object",
                                        "properties": {
                                            "language": {
                                                "type": "string"
                                            },
                                            "tz": {
                                                "type": "string"
                                            }
                                        }
                                    }
                                }
                            }, {
                                "required": ["region"],
                                "properties": {
                                    "region": {
                                        "enum": ["europe"]
                                    },
                                    "country": {
                                        "type": "string"
                                    },
                                    "language": {
                                        "type": "string"
                                    }
                                }
                            }
                        ]
                    }
                ]
            }
        }
    }
}

JSON Object to FAIL as both "asia" and "europe" type object cannot co-exist.

{
    "stat_data": [{
            "region": "america",
            "country": "USA",
            "states": "50"
        }, {
            "region": "asia",
            "country": "Japan",
            "details": {
                "language": "Japanese",
                "tz": "utc+9.00"
            }
        }, {
            "region": "europe",
            "country": "finland",
            "language": "Finnish"
        }
    ]
}

JSON Object to PASS as ONLY "asia" type object exist.

{
    "stat_data": [{
            "region": "america",
            "country": "USA",
            "states": "50"
        }, {
            "region": "asia",
            "country": "Japan",
            "details": {
                "language": "Japanese",
                "tz": "utc+9.00"
            }
        }
    ]
}

JSON Object to PASS as ONLY "europe" type object exist.

{
    "stat_data": [{
            "region": "america",
            "country": "USA",
            "states": "50"
        }, {
            "region": "europe",
            "country": "finland",
            "language": "Finnish"
        }
    ]
}

Upvotes: 4

Views: 4703

Answers (1)

Relequestual
Relequestual

Reputation: 12355

I can see why you tried the approach you did, however it does not work as expected because you've defined that, each item in the array may be america or (europe or asia), which isn't what you want.

Remember, items applies the value schema to EACH element in the array. It expresses no constraints on the overall array itself. contains checks that at least one of the items in the array validates against its value schema.

What you want is to say, each item in the array may have america or europe or asia, but the array may not contain europe if it contains asia, and the inverse.

I've refactored the schema and made some changes.

Hopefully you can also see the intent is clear in the use of oneOf >> (contains and not > contains).

JSON Schema works by adding constraints. You generally cannot define constraint by omission.

JSON Schema and data validation demo

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "containtsAsia": {
      "contains": {
        "properties": {
          "region": {
            "const": "asia"
          }
        }
      }
    },
    "containsEurope": {
      "contains": {
        "properties": {
          "region": {
            "const": "europe"
          }
        }
      }
    }
  },
  "type": "object",
  "properties": {
    "stat_data": {
      "type": "array",
      "minItems": 1,
      "items": {
        "type": "object",
        "properties": {
          "region": {
            "enum": [
              "america",
              "asia",
              "europe"
            ]
          },
          "country": {
            "type": "string"
          },
          "population": {
            "type": "string"
          }
        }
      },
      "oneOf": [
        {
          "allOf": [
            {
              "$ref": "#/definitions/containtsAsia"
            },
            {
              "not": {
                "$ref": "#/definitions/containsEurope"
              }
            }
          ]
        },
        {
          "allOf": [
            {
              "$ref": "#/definitions/containsEurope"
            },
            {
              "not": {
                "$ref": "#/definitions/containtsAsia"
              }
            }
          ]
        }
      ]
    }
  }
}

Upvotes: 5

Related Questions