0
votes

The fields in nodes depend on the value of entity. That is, if entity = "pd", then nodes has some fields, while entity = " top " - nodes has completely different fields, despite the fact that they are strictly required. For some reason, the JSON string is accepted by the valid schema, even if there are no fields defined in nodes as required. I already entire head broke, where can be mistake in the most scheme? JSON-schema:

{
  "definitions": {},
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "http://example.com/root.json",
  "type": "object",
  "title": "The Root Schema",
  "required": [
    "virtual"
  ],
  "properties": {
    "virtual": {
      "$id": "#/properties/virtual",
      "type": "array",
      "title": "The Virtual Schema",
      "items": {
        "$id": "#/properties/virtual/items",
        "type": "object",
        "title": "The Items Schema",
        "required": [
          "type",
          "path",
          "entity",
          "nodes"
        ],
        "properties": {
          "type": {
            "$id": "#/properties/virtual/items/properties/type",
            "type": "string",
            "title": "The Type Schema",
            "default": "",
            "examples": [
              "bus"
            ],
            "pattern": "^(.*)$"
          },
          "path": {
            "$id": "#/properties/virtual/items/properties/path",
            "type": "string",
            "title": "The Path Schema",
            "default": "",
            "examples": [
              "VBUS2"
            ],
            "pattern": "^(.*)$"
          },
          "entity": {
            "$id": "#/properties/virtual/items/properties/entity",
            "type": "string",
            "title": "The Entity Schema",
            "default": "",
            "examples": [
              "topaz"
            ],
            "enum": ["pde", "topaz"],
            "pattern": "^(.*)$"
          },
          "nodes": {
            "$id": "#/properties/virtual/items/properties/nodes",
            "type": "array",
            "title": "The Nodes Schema",
            "items": {
                "$id": "#/properties/virtual/items/properties/nodes/items",
                "type": "object",
                "title": "The Items Schema"
            }
          }
        }
      }
    }
  },
  "anyOf": [
    {       
        "if": {
            "properties": { "virtual": { "properties": { "entity": { "const": "topaz" } } } } 
        },
        "then": {
            "properties": { 
                "virtual": { 
                    "properties": { 
                        "nodes": {  
                            "items": {
                                "required": [
                                    "uid",
                                    "utype",
                                    "uaddress",
                                    "unozzles"
                                ],
                                "properties": {
                                    "uid": {
                                        "$id": "#/properties/virtual/items/properties/nodes/items/properties/uid",
                                        "type": "integer",
                                        "title": "The Uid Schema",
                                        "default": 0,
                                        "examples": [
                                            1
                                        ]
                                    },
                                    "utype": {
                                        "$id": "#/properties/virtual/items/properties/nodes/items/properties/utype",
                                        "type": "string",
                                        "title": "The Utype Schema",
                                        "default": "",
                                        "examples": [
                                            "dispenser"
                                        ],
                                        "pattern": "^(.*)$" 
                                    },
                                    "uaddress": {
                                        "$id": "#/properties/virtual/items/properties/nodes/items/properties/uaddress",
                                        "type": "string",
                                        "title": "The Uaddress Schema",
                                        "default": "",
                                        "examples": [
                                            "false"
                                        ],
                                        "pattern": "^(.*)$"
                                    },
                                    "unozzles": {
                                        "$id": "#/properties/virtual/items/properties/nodes/items/properties/unozzles",
                                        "type": "boolean",
                                        "title": "The Unozzles Schema",
                                        "default": false,
                                        "examples": [
                                            false
                                        ]
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    {
        "if": { 
            "properties": { "virtual": { "properties": { "entity": { "const" : "pde" } } } }
        },
        "then": {
            "properties": {
                "virtual": {
                    "properties": {
                        "nodes": {
                            "items": {
                                "required": [                                   
                                    "id",
                                    "type",
                                    "address",
                                    "nozzles"
                                ],
                                "properties": {
                                    "id": {                     
                                        "$id": "#/properties/virtual/items/properties/nodes/items/properties/id",
                                            "type": "string",
                                            "title": "The Id Schema",
                                            "default": "",
                                            "examples": [
                                                "vrt_1"
                                            ],
                                        "pattern": "^(.*)$"
                                    },
                                    "type": {
                                        "$id": "#/properties/virtual/items/properties/nodes/items/properties/type",
                                        "type": "string",
                                        "title": "The Type Schema",
                                        "default": "",
                                        "examples": [
                                            "dispenser"
                                        ],
                                        "pattern": "^(.*)$"
                                    },
                                    "address": {
                                        "$id": "#/properties/virtual/items/properties/nodes/items/properties/address",
                                        "type": "integer",
                                        "title": "The Address Schema",
                                        "default": 0,
                                        "examples": [
                                            1
                                        ]
                                    },
                                    "nozzles": {
                                        "$id": "#/properties/virtual/items/properties/nodes/items/properties/nozzles",
                                        "type": "array",
                                        "title": "The Nozzles Schema",
                                        "items": {
                                            "$id": "#/properties/virtual/items/properties/nodes/items/properties/nozzles/items",
                                            "type": "integer",
                                            "title": "The Items Schema",
                                            "default": 0,
                                            "examples": [
                                                1,
                                                2,
                                                3
                                            ]
                                        }
                                    }
                                }
                            }                                           
                        }
                    }
                }
            }
        }
    }
  ]  
}

This JSON is valid:

{
    "virtual": [
        {
            "type": "bus",
            "path": "VUS1",
            "entity": "pde",
            "nodes": [
                {
                    "id": "vrt_1",
                    "type": "string",
                    "address": 1,
                    "nozzles": [1, 2, 3]
                },
                {
                    "id": "vrt_2",
                    "type": "string",
                    "address": 2,
                    "nozzles": [1, 2, 3]
                }
            ]
        },
        {
            "type": "bus",
            "path": "VUS2",
            "entity": "topaz",
            "nodes": [          
                {
                    "uid": 1,
                    "utype": "string",
                    "uaddress": "false",
                    "unozzles": false
                },
                {
                    "uid": "vrt_1",
                    "utype": "string",
                    "uaddress": "false",
                    "unozzles": false
                }
            ]
        }
    ]
}

And this JSON should not be applied, but is considered valid:

{
    "virtual": [
        {
            "type": "bus",
            "path": "VUS1",
            "entity": "pde",
            "nodes": [
                {
                    "id_not_valid": "failure",
                    "type": 1,
                    "address": false,
                    "nozzles": [1, 2, 3]
                },
                {
                    "id": "vrt_2",
                    "type": "string",
                    "address": false,
                    "nozzles": [1, 2, 3]
                }
            ]
        },
        {
            "type": "bus",
            "path": "VUS2",
            "entity": "topaz",
            "nodes": [          
                {
                    "uid_not_valid": "failure",
                    "utype": 1,
                    "uaddress": "false",
                    "unozzles": false
                }
            ]
        }
    ]
}

In theory, the second JSON should not be validated. For several reasons:

  1. For entity= "pd", the required fields are "id", "type"," address "and"nozzles". In the second line of JSON instead the field "id" is replaced by the field "id_not_valid" - > the obligatory field " id " is absent and validation has to end in failure. The same for entity="top" - "the uid" is replaced by "id_not_valid"
  2. For entity= "pd", the address field is of type token, in the second JSON line it is set to false, which corresponds to the type "boolean", but validation still takes place (the same if you assign an array or string value to address). For entity="top" type, the type is string, but the integer value 1 assigned to it is also assumed by the validator to be the correct string.

But the online validators on the links below say that everything is OK and both JSON conform to the scheme.

The first site

Second site

The third website

So I believe there is an error in the scheme.
The scheme itself was made by this example Example of JSON schema compilation

Any comments and tips on fixing JSON-schema, please

1
Could you edit your theory of why the second instance should fail, please? I'm not sure that it came out quite as you intended.gregsdennis
Edited the question. I hope this make it clearer?Egor Vasilyev

1 Answers

1
votes

The schema is malformed.

(I'm ignoring the fact that the schema states entity should be "pde" or "topaz", but the instances have "pd" and "top". I assume this is a typo.)

Inside the anyOf, you have two items, each with an if conditional keyword. The schema presented by this keyword is

{
  "properties": {
    "virtual": {
      "properties": {
        "entity": {
          "const": "topaz"
        }
      }
    }
  }
}

This is saying that if virtual has an entity property, then it should be "topaz". But the way that properties works is that it only fails validation if the instance is an object. However in #/properties, you declare that virtual should be an an array of objects where each item contains an entity property.

Since virtual is an array in your instance, none of the if condition keywords in the anyOf pass, so they defer to the else keywords for those subschemas, which don't exist (so the pass by default). This results in both subschemas for the anyOf passing.

I think what you're trying to do is validate each of the items inside the array based on the value of the entity property for that item. This means that you could have both a pde item and a topaz item in the array.

To do this you need to isolate where the variance is. In your case, it's the item level inside the virtual array. This is where you need to put your anyOf.

So you'll want to add your anyOf to #/properties/virtual/items. This is the only point in the schema where an if/then construct can key off of the entity property and enforce the nodes property.


Edit Things I would change

  1. Remove all of the internal $id declarations. They only reiterate the location in the document and provide no additional functionality.
  2. Remove the type and pattern declarations from entity. enum is sufficient here because it declares that the values must be one of the items in the array. Since these are both strings and match the given pattern, those keywords are redundant.
  3. Move the anyOf alongside the properties keyword inside virtual and change it to a oneOf. This is the most specific location where you can access both the entities property and the nodes property. Changing it to a oneOf ensures that exactly one can be true.
  4. Drop the if/then construct and just include the constant value in the then portion.

In the end, it would be structured something like this:

{
  ... ,
  "properties": {
    "virtual": {
      "type": "array",
      "title": "The Virtual Schema",
      "items": {
        "type": "object",
        "title": "The Items Schema",
        "required": [ "type", "path", "entity", "nodes" ],
        "properties": {
          "type": { ... },
          "path": { ... },
          "entity": {
            "title": "The Entity Schema",
            "default": "",
            "examples": [
              "topaz"
            ],
            "enum": ["pde", "topaz"]
          }
        },
        "oneOf": [
            {
                "properties": {
                    "entity": {"const": "topaz"},
                    "nodes": { ... }
                }
            },
            {
                "properties": {
                    "entity": {"const": "pde"},
                    "nodes": { ... }
                }
            }
        ]
      }
    }
  } 
}

Here, we're declaring that the items within the virtual array must be objects requiring 4 properties: type, path, entity, and nodes. We explicitly define type, path, entity using the properties keyword. But we conditionally define the nodes property using the oneOf and specifying a constant value for the entity property in each case.