Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ajv conditional schema validation based on data

Tags:

json

schema

ajv

I would like to specify a regexp pattern for one field based on the data in another. Is this possible? I've tried switch and $data but not sure how to use them. for example, if data looks like:

{
   "contacts":[
      {
         "mode":"Email",
         "contact":"[email protected]"
      },
      {
         "mode":"Phone",
         "contact":"111-555-1234"
      }
   ]
}

and schema looks something like:

"$schema":"http://json-schema.org/draft-04/schema#",
   "type":"object",
   "properties":{
      "Contacts":{
         "type":"array",
         "minItems":1,
         "items":{
            "type":"object",
            "properties":{
               "mode":{
                  "type":"string",
                  "enum":[
                     "Email",
                     "Phone"
                  ]
               },
               "contact":{
                  "type":"string",
                  "pattern":"?????"
               }
            },
            "required":[
               "mode",
               "contact"
            ]
         }
      }
   }
}

How can I set the pattern of contact based on data in mode, so that if mode is Email, it validates contact against a regexp for an email format, and if mode is Phone, it validates contact against a regexp for a phone format? I have the regexp for each. I need the logic to choose one or the other.

like image 426
jmaynard Avatar asked Mar 20 '17 16:03

jmaynard


People also ask

What is schema based validation?

Schemas are a special, object-based way of defining validations or sanitizations on requests. At the root-level, you specify field paths as keys, and objects as values -- which define the error messages, locations and validations/sanitizations.

How do you validate a JSON file against a schema?

The simplest way to check if JSON is valid is to load the JSON into a JObject or JArray and then use the IsValid(JToken, JsonSchema) method with the JSON Schema. To get validation error messages, use the IsValid(JToken, JsonSchema, IList<String> ) or Validate(JToken, JsonSchema, ValidationEventHandler) overloads.


1 Answers

There are several ways to do it

anyOf (pros: draft-04 compatible, cons: error reporting is a bit verbose - you will get errors from both subschemas if none matches):

{
   "type": "object",
   "properties": {
      "Contacts": {
         "type": "array",
         "minItems": 1,
         "items": {
            "type": "object",
            "anyOf": [
               {
                  "properties": {
                     "mode": {"enum": ["Email"]},
                     "contact": {
                        "type": "string",
                        "format": "email"
                     }
                  }
               },
               {
                  "properties": {
                     "mode": {"enum": ["Phone"]},
                     "contact": {
                        "type": "string",
                        "pattern": "phone_pattern"
                     }
                  }
               }
            ],
            "required": ["mode", "contact"]
         }
      }
   }
}

if/then/else (available in ajv-keywords package, pros: error reporting makes more sense, accepted to be included in draft-07, cons: not standard at the moment):

{
   "type": "object",
   "properties": {
      "Contacts": {
         "type": "array",
         "minItems": 1,
         "items": {
            "type": "object",
            "properties": {
               "mode": {"type": "string", "enum": ["Email", "Phone"]},
               "contact": {"type": "string"}
            },
            "if": {
               "properties": {
                  "mode": {"enum": ["Email"]}
               }
            },
            "then": {
               "properties": {
                  "contact": {"format": "email"}
               }
            },
            "else": {
               "properties": {
                  "contact":  {"pattern": "phone_pattern"}
               }
            }
            "required": ["mode", "contact"]
         }
      }
   }
}

select (available in ajv-keywords package, pros: more concise than if/then/else, particularly if there are more than two possible values, cons: not on the standard track yet, but you can support it :), requires enabling $data reference and Ajv v5.x.x):

{
   "type": "object",
   "properties": {
      "Contacts": {
         "type": "array",
         "minItems": 1,
         "items": {
            "type": "object",
            "properties": {
               "mode": {"type": "string"},
               "contact": {"type": "string"}
            },
            "select": { "$data": "0/mode" },
            "selectCases": {
               "Email": {
                  "properties": {
                     "contact": {"format": "email"}
                  }
               },
               "Phone": {
                  "properties": {
                     "contact": {"pattern": "phone_pattern"}
                  }
               }
            },
            "selectDefault": false,
            "required": ["mode", "contact"]
         }
      }
   }
}

I prefer the last option.

like image 187
esp Avatar answered Sep 28 '22 00:09

esp