Anbarasan
Anbarasan

Reputation: 1207

JSON Schema - Dynamic required properties

I have a schema to validate an incoming JSON,

JSON Schema

{
    'title': 'storage schema',
    'description': 'storage schema',
    'type': 'object',
    'properties':{
        'title': {
            'title':'storage Name',
            'type': 'string',
            'minLength': 1,
            'maxLength': 255
        },
        'storageType': {
            'title': 'storage Type',
            'enum' : ['DVD', 'HDD', 'Network', 'Internet']
        },
        'minCapacity': {
            'title': 'Minimum Storage Capacity',
            'type': 'number'
        },
        'maxCapacity': {
            'title': 'Maximum Storage Capacity',
            'type': 'number'
        }
    },
    'additionalProperties':false,
    'required':['title', 'storageType']
}

I would like to have minCapacity and maxCapacity properties to be present in the json if storageType is DVD or HDD and not present in the json if the storageType is Network.

It can be done if i modify the schema to have storage as object and have min and max capacity as its properties, as in the schema below.

{
    'title': 'storage schema',
    'description': 'storage schema',
    'type': 'object',
    'properties':{
        'title': {
            'title':'storage Name',
            'type': 'string',
            'minLength': 1,
            'maxLength': 255
        },
        'storage': {
            'title': 'storage Details',
            'type': 'object',
            'oneOf' : [{'$ref': '#/storage/disk'},
                       {'$ref': '#/storage/network'}]
        },
    },
    'additionalProperties':false,
    'required':['title', 'storage'],
    'storage':{
        'disk':{
            'properties':{
                'type': {
                    'title': 'Storage Type',
                    'enum': ['HDD', 'DVD']
                },
                'minCapacity': {
                    'title': 'Minimum Storage Capacity',
                    'type': 'number'
                },
                'maxCapacity': {
                    'title': 'Maximum Storage Capacity',
                    'type': 'number'
                }
            },
            'additionalProperties': false,
            'required':['type', 'minCapacity', 'maxCapacity']
        },
        'network':{
            'properties':{
                'type': {
                    'title': 'Storage Type',
                    'enum': ['Network', 'Internet']
                }
            },
            'additionalProperties': false,
            'required':['type']
        }
    }
}

But I would like to achieve this without changing the structure of the schema.

Can it be done?

Valid Json 1

{
    'title': 'additional mandatory properties',
    'storageType': 'HDD',
    'minCapacity': 0.1,
    'maxCapacity': 1
}

Valid Json 2

{
    'title': 'no additional mandatory properties',
    'storageType': 'Network'
}

Invalid Json 1

{
    'title': 'additional mandatory properties',
    'storageType': 'Internet',
    'minCapacity': 0.1,
    'maxCapacity': 1
}

Invalid Json 2

{
    'title': 'no additional mandatory properties',
    'storageType': 'HDD'
}

UPDATE

when trying schema from jason's answer, it was not validating when only, one of the non required parameters is present in the json. The invalid JSON are given below.

Invalid Json 3

{
    'title': 'additional mandatory properties',
    'storageType': 'Internet',
    'minCapacity': 0.1
}

Invalid Json 4

{
    'title': 'additional mandatory properties',
    'storageType': 'Internet',
    'maxCapacity': 1
}

I solved this problem with a small modification to the schema in the not required part, which is as below.

{
    "title": "storage schema",
    "description": "storage schema",
    "type": "object",
    "properties": {
        "title": {
            "title": "storage Name",
            "type": "string",
            "minLength": 1,
            "maxLength": 255
        },
        "storageType": {
            "title": "storage Type"
        },
        "minCapacity": {
            "title": "Minimum Storage Capacity",
            "type": "number"
        },
        "maxCapacity": {
            "title": "Maximum Storage Capacity",
            "type": "number"
        }
    },
    "required": ["title", "storageType"],
    "anyOf": [
        {
            "properties": {
                "storageType": {
                    "enum": ["DVD", "HDD"]
                }
            },
            "required": ["minCapacity", "maxCapacity"]
        },
        {
            "properties": {
                "storageType": {
                    "enum": ["Network", "Internet"]
                }
            },
            "allOf":[
                {"not": {"required": ["maxCapacity"]}},
                {"not": {"required": ["minCapacity"]}}
            ]

        }
    ]
}

Upvotes: 1

Views: 2904

Answers (1)

Jason Desrosiers
Jason Desrosiers

Reputation: 24489

Here is the solution.

{
  "title": "storage schema",
  "description": "storage schema",
  "type": "object",
  "properties": {
    "title": {
      "title": "storage Name",
      "type": "string",
      "minLength": 1,
      "maxLength": 255
    },
    "storageType": {
      "title": "storage Type"
    },
    "minCapacity": {
      "title": "Minimum Storage Capacity",
      "type": "number"
    },
    "maxCapacity": {
      "title": "Maximum Storage Capacity",
      "type": "number"
    }
  },
  "additionalProperties": false,
  "required": ["title", "storageType"],
  "anyOf": [
    {
      "properties": {
        "storageType": {
          "enum": ["DVD", "HDD"]
        }
      },
      "required": ["minCapacity", "maxCapacity"]
    },
    {
      "properties": {
        "storageType": {
          "enum": ["Network", "Internet"]
        }
      },
      "not": { "required": ["maxCapacity", "minCapacity"] }
    }
  ]
}

P.S. Use of "additionalProperties": false is discouraged. Having to things like #/anyOf/1/not is an example of how this feature can be more trouble than it's worth. Best practice is to simply ignore properties that don't belong.

Upvotes: 1

Related Questions