简体   繁体   中英

JSON Schema - How to apply conditionals based on boolean and further restrict with additionalProperties: false?

Ansible picked up support for jsonschema, and I have a variety of use cases for it, but most all of them require conditionals. I have been reading the doc's for jsonschema and draft-7 if-then-else, and tried several permutations, and seemingly only get half of my target handled. Hoping someone can help pave the way with a practical example for me that I can deconstruct.

Two possibilities based on the bool in "send_notifications":

{
    "capture_state": "value here",
    "send_notifications": true,
    "user_email_list": ["email@something.com", "email2@something.com"],
    "host_info": [
        {
            "ansible_host": "192.20.xxx.xxx",
            "ECRTicket": "1103035"
        },
    .... continued
    ]
}

or

{
    "capture_state": "value here",
    "send_notifications": false
}

Essentially, only allow, and require the properties shown in the examples based on the bool in "send_notifications", and unconditionally fail if its neither of these two schemas. I'll likely improve this more with patterns as well, but just these keys and types are a great start.

I had initially generated the following base schema to use as a reference to manipulate and add the conditionals, and perhaps it is something in this schema that conflicts with using conditionals and additionalProperties:false, but being new to jsonschema it's not immediately obvious to me, so reaching out for some assistance so I can put this to use more often. Any help would be appreciated, thank you for your time, new to posting on stack, if I can improve my question please let me know.

{
    "$schema": "http://json-schema.org/draft-07/schema",
    "type": "object",
    "required": [
        "capture_state",
        "send_notifications",
        "user_email_list",
        "host_info"
    ],
    "properties": {
        "capture_state": {
            "type": "string"
        },
        "send_notifications": {
            "type": "boolean"
        },
        "user_email_list": {
            "type": "array",
            "items": {
                "anyOf": [
                    {
                        "type": "string"
                    }
                ]
            }
        },
        "host_info": {
            "type": "array",
            "items": {
                "anyOf": [
                    {
                        "type": "object",
                        "required": [
                            "ansible_host",
                            "ECRTicket"
                        ],
                        "properties": {
                            "ansible_host": {
                                "type": "string"
                            },
                            "ECRTicket": {
                                "type": "string"
                            }
                        },
                        "additionalProperties": false
                    }
                ]
            }
        }
    },
    "additionalProperties": false
}

Generally, you're going to have a hard time whenever you choose to restrict unknown properties. I'll assume you have a good reason and including it is worth it.

There are a couple ways to do this. I'm presenting it this way because it produces the best messages when a JSON instance fails schema validation.

{
  "$schema": "http://json-schema.org/draft-07/schema",

  "type": "object",
  "properties": {
    "capture_state": { "type": "string" },
    "send_notifications": { "type": "boolean" },
  },
  "required": ["capture_state", "send_notifications"],

  "if": {
    "properties": {
      "send_notifications": { "const": true },
    },
    "required": ["send_notifications"]
  },
  "then": {
    "properties": {
      "capture_state": true,
      "send_notifications": true,
      "user_email_list": {
        "type": "array",
        "items": { "type": "string" }
      },
      "host_info": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "ansible_host": { "type": "string" },
            "ECRTicket": { "type": "string" }
          },
          "required": ["ansible_host", "ECRTicket"],
          "additionalProperties": false
        }
      }
    },
    "required": ["user_email_list", "host_info"],
    "additionalProperties": false
  },
  "else": {
    "properties": {
      "capture_state": true,
      "send_notifications": true
    },
    "additionalProperties": false
  }
}

additionalProperties only recognizes properties declared in the same sub-schema it appears in. That means you need to include some dummy properties entries so it knows about the properties that are declared elsewhere in the schema.

I assume ansible doesn't support more recent versions of JSON Schema than draft-07? If it does, you can use unevaluatedProperties instead of additionalProperties to avoid jumping through some of the hoops you have to with additionalProperties .

{
  "$schema": "http://json-schema.org/draft-07/schema",

  "type": "object",
  "properties": {
    "capture_state": { "type": "string" },
    "send_notifications": { "type": "boolean" },
  },
  "required": ["capture_state", "send_notifications"],
  "unevaluatedProperties": false,

  "if": {
    "properties": {
      "send_notifications": { "const": true },
    },
    "required": ["send_notifications"]
  },
  "then": {
    "properties": {
      "user_email_list": {
        "type": "array",
        "items": { "type": "string" }
      },
      "host_info": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "ansible_host": { "type": "string" },
            "ECRTicket": { "type": "string" }
          },
          "required": ["ansible_host", "ECRTicket"],
          "additionalProperties": false
        }
      }
    },
    "required": ["user_email_list", "host_info"]
  }
}

If anyone at Ansible happens to see this, draft 2020-12 includes unevaluatedProperties and will get a lot of implementation support as it is also included in OpenAPI 3.1.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM