user8479984
user8479984

Reputation: 501

Extra error message by JSON Schema validator when if-else is there

Here is my JSON Schema(hypothetical, as I cannot share my actual one) and the JSON. The if-then-else condition is like:

at a time only one (1) out of three (3) country can appear to define statistics.

Schema

    {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "type": "object",
        "required": [
            "year",
            "country",
            "statistics"
        ],
        "definitions": {
        "statisticsUsa": {
          "if": {
            "properties": {
              "type": {
                "const": "statisticsUsa"
              }
            },
            "required": [
              "type"
            ]
          },
          "then": {
            "properties": {
              "type": {
                "const": "statisticsUsa"
              }
            }
          }
        },
            "statisticsFinland": {
          "if": {
            "properties": {
              "type": {
                "const": "statisticsFinland"
              }
            },
            "required": [
              "type"
            ]
          },
          "then": {
            "properties": {
              "type": {
                "const": "statisticsFinland"
              }
            }
          }
        },
        "statisticsGermany": {
          "if": {
            "properties": {
              "type": {
                "const": "statisticsGermany"
              }
            },
            "required": [
              "type", "literacy"
            ]
          },
          "then": {
            "properties": {
              "type": {
                "const": "statisticsGermany"
              },
              "literacy": {
                "type": "string"
              }
            }
          }
        }
        },
        "properties": {
            "year": {
                "type": "string"
            },
            "country": {
                "type": "string",
                "enum": [
                    "USA",
                    "FINLAND",
                    "GERMANY"
                ]
            },
            "statistics": {

                "type": "array",
                "allOf": [{
                        "if": {
                            "contains": {
                                "type": "object",
                                "properties": {
                                    "type": {
                                        "type": "string",
                                        "enum": [
                                            "statisticsUsa"
                                        ]
                                    }
                                }
                            }
                        },
                        "then": {
                            "not": {
                                "contains": {
                                    "type": "object",
                                    "properties": {
                                        "type": {
                                            "type": "string",
                                            "enum": [
                                                "statisticsFinland",
                                                "statisticsGermany"
                                            ]
                                        }
                                    }
                                }
                            }
                        },"errorMessage" :"Only USA applicable at a time for statistics"
                    },
                    {
                        "if": {
                            "contains": {
                                "type": "object",
                                "properties": {
                                    "type": {
                                        "type": "string",
                                        "enum": [
                                            "statisticsFinland"
                                        ]
                                    }
                                }
                            }
                        },
                        "then": {
                            "not": {
                                "contains": {
                                    "type": "object",
                                    "properties": {
                                        "type": {
                                            "type": "string",
                                            "enum": [
                                                "statisticsUsa",
                                                "statisticsGermany"
                                            ]
                                        }
                                    }
                                }
                            }
                        },"errorMessage" :"Only Finland applicable at a time for statistics"
                    },
                    {
                        "if": {
                            "contains": {
                                "type": "object",
                                "properties": {
                                    "type": {
                                        "type": "string",
                                        "enum": [
                                            "statisticsGermany"
                                        ]
                                    }
                                }
                            }
                        },
                        "then": {
                            "not": {
                                "contains": {
                                    "type": "object",
                                    "properties": {
                                        "type": {
                                            "type": "string",
                                            "enum": [
                                                "statisticsUsa",
                                                "statisticsFinland"
                                            ]
                                        }
                                    }
                                }
                            }
                        },"errorMessage" :"Only Germany applicable at a time for statistics"
                    }

                ],
                "minItems": 1,
                "items": {
                    "anyOf": [{
                            "$ref": "#/definitions/statisticsUsa"
                        },
                        {
                            "$ref": "#/definitions/statisticsFinland"
                        },
                        {
                            "$ref": "#/definitions/statisticsGermany"
                        }
                    ]
                }
            }
        }
    }

Json 1

    const Ajv = require('ajv');
    const ajv = new Ajv({
        allErrors: true,
        jsonPointers: true
    });

    const schema1 = require("./sof.json");
    require('ajv-errors')(ajv /*, {singleError: true} */);
    const data = {
      year: "2000",
      country: "USA",
      statistics: [
        {"type":"statisticsUsa"},
        {"type":"statisticsFinland"}
      ]
    }

    let valid = ajv.validate(schema1, data); //schema, data
    if (!valid) {
      console.log(`${JSON.stringify(ajv.errors,null,2)}`);
    }

Here, I can see 2 errors as part of the array, when I have 2 countries. My question is, being 'finland' cannot have with 'USA', the error for "Only USA applicable at a time for statistics" is NOT acceptbale, as it satisfies the condition. But, The error for Findland is acceptable.

    [
      {
        "keyword": "errorMessage",
        "dataPath": "/statistics",
        "schemaPath": "#/properties/statistics/allOf/0/errorMessage",
        "params": {
          "errors": [
            {
              "keyword": "not",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/0/then/not",
              "params": {},
              "message": "should NOT be valid"
            },
            {
              "keyword": "if",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/0/if",
              "params": {
                "failingKeyword": "then"
              },
              "message": "should match \"then\" schema"
            }
          ]
        },
        "message": "Only USA applicable at a time for statistics"
      },
      {
        "keyword": "errorMessage",
        "dataPath": "/statistics",
        "schemaPath": "#/properties/statistics/allOf/1/errorMessage",
        "params": {
          "errors": [
            {
              "keyword": "not",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/1/then/not",
              "params": {},
              "message": "should NOT be valid"
            },
            {
              "keyword": "if",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/1/if",
              "params": {
                "failingKeyword": "then"
              },
              "message": "should match \"then\" schema"
            }
          ]
        },
        "message": "Only Finland applicable at a time for statistics"
      }
    ]

Also, if I add 'Germany' in JSON as,

{
  year: "2000",
  country: "USA",
  statistics: [
    {"type":"statisticsUsa"},
    {"type":"statisticsFinland"},
    {"type":"statisticsGermany"}
  ]
}

it throws 3 errors as part of the array (See below). Why?

  1. Technically, the schema validator should stop at 2nd item and should ignore the 'Germany' in the error array as it finds 'Findland' entry is violating the if-else conditions.
  2. Also, does schema-validator takes each item from 'statistics' array (from the JSON) and checks the conditions within 'allOf' for every run and that's why 3 error entries in the array. Is my understanding correct. (If you see the first error array above, it has 2 only entries)

    [
      {
        "keyword": "errorMessage",
        "dataPath": "/statistics",
        "schemaPath": "#/properties/statistics/allOf/0/errorMessage",
        "params": {
          "errors": [
            {
              "keyword": "not",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/0/then/not",
              "params": {},
              "message": "should NOT be valid"
            },
            {
              "keyword": "if",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/0/if",
              "params": {
                "failingKeyword": "then"
              },
              "message": "should match \"then\" schema"
            }
          ]
        },
        "message": "Only USA applicable at a time for statistics"
      },
      {
        "keyword": "errorMessage",
        "dataPath": "/statistics",
        "schemaPath": "#/properties/statistics/allOf/1/errorMessage",
        "params": {
          "errors": [
            {
              "keyword": "not",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/1/then/not",
              "params": {},
              "message": "should NOT be valid"
            },
            {
              "keyword": "if",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/1/if",
              "params": {
                "failingKeyword": "then"
              },
              "message": "should match \"then\" schema"
            }
          ]
        },
        "message": "Only Finland applicable at a time for statistics"
      },
      {
        "keyword": "errorMessage",
        "dataPath": "/statistics",
        "schemaPath": "#/properties/statistics/allOf/2/errorMessage",
        "params": {
          "errors": [
            {
              "keyword": "not",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/2/then/not",
              "params": {},
              "message": "should NOT be valid"
            },
            {
              "keyword": "if",
              "dataPath": "/statistics",
              "schemaPath": "#/properties/statistics/allOf/2/if",
              "params": {
                "failingKeyword": "then"
              },
              "message": "should match \"then\" schema"
            }
          ]
        },
        "message": "Only Germany applicable at a time for statistics"
      }
    ]

Upvotes: 1

Views: 1818

Answers (1)

Carsten
Carsten

Reputation: 2147

You already deduced it yourself: the allOf contains applies all subschemas and checks against all array items.

If you want only one error message, then you should decide the priority of countries and only check in subsequent allOf parts for the remaining countries and not all of them each time. E.g.

  1. If the array contains a “USA” entry, neither “GERMANY” nor “FINLAND” should be present (as you already have it)
  2. If the array contains a “GERMANY” entry, no “FINLAND” entry should be present (do not check for “USA” again)
  3. If the array contains a “FINLAND” entry, the possible scenarios are already covered by the two checks above – no need for any additional check here.

That way, you never get multiple of these errors at the same time.

Upvotes: 1

Related Questions