JSON-схема с несколькими вложенными anyOf - PullRequest
0 голосов
/ 11 ноября 2019

На основании предыдущего ответа я построил схему, которая отвечала бы моим требованиям. Вопрос и ответ на него можно увидеть здесь .

Полученная схема:

{   
    "definitions": {},
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "required": [
        "virtual"
    ],
    "properties": {
        "virtual": {
            "type": "array",
            "items": {
                "type": "object",
                "required": [
                    "type",
                    "path",
                    "entity",
                    "nodes"
                ],
                "properties": {
                    "type": {
                        "type": "string"
                    },
                    "path": {
                        "type": "string"
                    },
                    "entity": {
                        "enum": ["pde", "topaz"]
                    }         
                },
                "anyOf": [
                    {
                        "properties": {
                            "entity": {"const": "pde"},
                            "nodes": {
                                "type": "array",
                                "items": {
                                    "type": "object",
                                    "title": "The Items Schema",
                                    "required": [
                                        "id",
                                        "type",
                                        "address",
                                        "nozzles"
                                    ],
                                    "properties": {
                                        "id": {
                                            "type": "string"
                                        },
                                        "type": {
                                            "type": "string"
                                        },
                                        "address": {
                                            "type": "integer"
                                        },
                                        "nozzles": {
                                            "type": "array",
                                            "items": {
                                                "type": "integer"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    {
                        "properties": {
                            "entity": {"const": "topaz"},
                            "nodes": {
                                "type": "array",
                                "items": {
                                    "type": "object",
                                    "required": [
                                        "uid",
                                        "utype",
                                        "uaddress",
                                        "unozzles"
                                    ],
                                    "properties": {
                                        "uid": {
                                            "type": "integer"
                                        },
                                        "utype": {                  
                                            "type": "string"
                                        },
                                        "uaddress": {
                                            "type": "string"
                                        },
                                        "unozzles": {
                                            "type": "boolean"
                                        }
                                    }
                                }
                            }
                        } 
                    }
                ]
            }
        }
    }
}

И JSON:

{
    "virtual": [
        {
            "type": "bus",
            "path": "VBUS1",
            "entity": "pde",
            "nodes": [
                {
                    "id": "vrt_1",
                    "type": "dispenser",
                    "address": 1,
                    "nozzles": [1, 2, 3]
                },
                {
                    "id": "vrt_2",
                    "type": "dispenser",
                    "address": 2,
                    "nozzles": [4, 5, 3]
                }
            ]
        },
        {
            "type": "bus",
            "path": "VBUS2",
            "entity": "topaz",
            "nodes": [          
                {
                    "uid": 1,
                    "utype": "dispenser",
                    "uaddress": "false",
                    "unozzles": true
                },
                {
                    "uid": 2,
                    "utype": "dispenser",
                    "uaddress": "true",
                    "unozzles": false
                }
            ]
        }
    ]
}

появилась следующая проблема. Когда тип = шина, JSON имеет поля пути и сущности. Но, если type = io, поля пути и сущности отсутствуют, а поле узла выглядит не так, как в предыдущих двух.

Следовательно, мне нужно иметь anyOf, который бы отслеживал значение поля типа, и другое anyOf, которое быработа для type = bus.

Я постараюсь объяснить более четко. Необходимо отслеживать значение поля типа и, если оно равно bus, то появляются поля пути и сущности. В зависимости от значения объекта поле узла имеет определенную структуру (в точности то, что написано на диаграмме выше).

Я пытался создать схему с вложенным anyOf:

{
    "definitions": {},
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "required": [
        "virtual"
    ],
    "properties": {
        "virtual": {
            "type": "array",
            "items": {
                "type": "object",
                "required": [
                    "type"
                ],
                "properties": {
                    "type": {
                        "enum": ["bus", "io"]
                    }
                },
                "anyOf": [
                    {

                        "properties": {
                            "type": {"const": "bus"},
                            "path": { "type": "string" },
                            "entity": { "enum": ["topaz", "pde"] }
                        },
                        "anyOf":[
                        {
                            "properties":{
                                "entity": {"const": "pde"},
                                "nodes": {
                                    "type": "array",
                                    "items": {
                                        "type": "object",
                                        "required": [
                                            "id",
                                            "type",
                                            "address",
                                            "nozzles"
                                        ],
                                        "properties": {
                                            "id": { "type": "string" },
                                            "type": { "type": "string" },
                                            "address": { "type": "integer" },
                                            "nozzles": {
                                                "type": "array",
                                                "items": { "type": "integer" }
                                            }
                                        }
                                    }
                                }
                            }
                        },
                        {
                            "entity": {"const": "topaz"},
                            "nodes": {
                                "type": "array",
                                "items": {
                                    "type": "object",
                                    "required": [
                                        "uid",
                                        "utype",
                                        "uaddress",
                                        "unozzles"
                                    ],
                                    "properties": {
                                        "uid": { "type": "integer" },
                                        "utype": { "type": "string" },
                                        "uaddress": { "type": "string" },
                                        "unozzles": { "type": "boolean" }
                                    }
                                }
                            }
                        }
                        ]
                    },
                    {
                        "properties": {
                            "type": {"const": "io"},
                            "nodes": {
                                "type": "array",
                                "items":{
                                    "type": "object",
                                    "required": [
                                        "num",
                                        "key",
                                        "title",
                                        "path"
                                    ],
                                    "properties": {
                                        "num": { "type": "integer" },
                                        "key": { "type": "integer" },
                                        "title": { "type": "string" },
                                        "path": { "type": "string" }
                                    }
                                }
                            }
                        }
                    }
                ]
            }
        }   
    }
} 

Пример проверки схемы на сайте

Но, как и ожидалось, схема принимает даже те схемы, которые не должны приниматься.

Пример недействительной схемы,Нет обязательного поля идентификатора для type = bus, entity = pde. Не существует обязательного поля uid для type = bus, entity = topaz. Такое ощущение, что второе вложенное anyOf игнорируется.

{
  "virtual": [
    {
      "type": "bus",
      "path": "VBUS1",
      "entity": "pde",
      "nodes": [
        {
          "not_id": "vrt_1",
          "type": "dispenser",
          "address": 1,
          "nozzles": [
            1,
            2,
            3
          ]
        },
        {
          "id": "vrt_2",
          "type": "dispenser",
          "address": 2,
          "nozzles": [
            4,
            5,
            3
          ]
        }
      ]
    },
    {
      "type": "bus",
      "path": "VBUS2",
      "entity": "topaz",
      "nodes": [
        {
          "not_uid": 1,
          "utype": "dispenser",
          "uaddress": "false",
          "unozzles": true
        },
        {
          "uid": 2,
          "utype": "dispenser",
          "uaddress": "true",
          "unozzles": false
        }
      ]
    },
    {
      "type": "io",
      "nodes": [
        {
          "num": 1,
          "key": 123,
          "title": "123",
          "path": "123"
        }
      ]
    }
  ]
}

Проверьте правильность неверного JSON .

В этом случае anyOf для поля типа работает какожидается. Я предполагаю, что эта проблема возникла снова из-за неправильного расположения схемы, но я не смог найти информацию в Интернете о вложенном anyOf.

При попытке вставить все чеки в один anyOf все работает, как задумано.

{   
    "definitions": {},
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "required": [
        "virtual"
    ],
    "properties": {
        "virtual": {
            "type": "array",
            "items": {
                "type": "object",
                "required": [
                    "type"
                ],
                "properties": {
                    "type": { "enum": ["bus", "io"] }        
                },
                "anyOf": [
                    {
                        "properties": {
                            "type": {"const": "bus"},
                            "path": { "type": "string" },
                            "entity": {"const": "pde"},
                            "nodes": {
                                "type": "array",
                                "items": {
                                    "type": "object",
                                    "title": "The Items Schema",
                                    "required": [
                                        "id",
                                        "type",
                                        "address",
                                        "nozzles"
                                    ],
                                    "properties": {
                                        "id": { "type": "string" },
                                        "type": { "type": "string" },
                                        "address": { "type": "integer" },
                                        "nozzles": {
                                            "type": "array",
                                            "items": { "type": "integer" }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    {
                        "properties": {
                            "type": {"const": "bus"},
                            "path": { "type": "string" },
                            "entity": {"const": "topaz"},
                            "nodes": {
                                "type": "array",
                                "items": {
                                    "type": "object",
                                    "required": [
                                        "uid",
                                        "utype",
                                        "uaddress",
                                        "unozzles"
                                    ],
                                    "properties": {
                                        "uid": { "type": "integer" },
                                        "utype": { "type": "string" },
                                        "uaddress": { "type": "string" },
                                        "unozzles": { "type": "boolean" }
                                    }
                                }
                            }
                        } 
                    },
                    {
                        "properties": {
                            "type": {"const": "io"},
                            "nodes": {
                                "type": "array",
                                "items": {
                                    "type": "object",
                                    "required": [
                                        "num",
                                        "key",
                                        "title",
                                        "path"
                                    ],
                                    "properties": {
                                        "num": { "type": "integer" },
                                        "key": { "type": "integer" },
                                        "title": { "type": "string" },
                                        "path": { "type": "string" }
                                    }
                                }
                            }
                        } 
                    }
                ]
            }
        }
    }
}

Но:

  1. Эта схема выглядит довольно грязной
  2. Это не совсем то, что я хотел бы видеть

Сама схема JSON, для которой вы хотите создать схему:

{
    "virtual": [
        {
            "type": "bus",
            "path": "VBUS1",
            "entity": "pde",
            "nodes": [
                {
                    "id": "vrt_1",
                    "type": "dispenser",
                    "address": 1,
                    "nozzles": [1, 2, 3]
                },
                {
                    "id": "vrt_2",
                    "type": "dispenser",
                    "address": 2,
                    "nozzles": [4, 5, 3]
                }
            ]
        },
        {
            "type": "bus",
            "path": "VBUS2",
            "entity": "topaz",
            "nodes": [          
                {
                    "uid": 1,
                    "utype": "dispenser",
                    "uaddress": "false",
                    "unozzles": true
                },
                {
                    "uid": 2,
                    "utype": "dispenser",
                    "uaddress": "true",
                    "unozzles": false
                }
            ]
        },
        {
            "type": "io",
            "nodes": [
                "num": 4,
                "key": 123456,
                "title": "io",
                "path": "default"
            ]
        }
    ]
}

Полный JSON имеет довольно сложную структуру, и здесь представлена ​​только ее часть. Поэтому я хотел бы понять, как правильно структурировать такие вещи (понять саму идею и, желательно, увидеть пример правильной схемы. Хотя бы схематично).

Итак, подведем итог. Мне нужно понять, как любой из может быть реализован в одном из вариантов anyOf. Это возможно? И, если да, где я могу увидеть примеры и инструкции по составлению таких схем? Если нет, есть ли обходной путь?

Ответы [ 2 ]

1 голос
/ 12 ноября 2019

Я думаю, что нашел решение. Однако, если есть какие-либо комментарии или исправления - буду рад услышать.

На всякий случай приведу пример полученной схемы:

{   
    "definitions": {},
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "required": [
        "virtual"
    ],
    "properties": {
        "virtual": {
            "type": "array",
            "items": {
                "type": "object",
                "required": [
                    "type"
                ],
                "properties": {
                    "type": {
                        "enum": ["bus", "io"]
                    }
                },
                "anyOf": [
                    {       
                        "properties":{
                            "type": {"const": "bus"},                       
                        },
                        "anyOf":[
                            {
                                "properties":{
                                    "path": { "type": "string" },
                                    "entity": {"const": "pde"},
                                    "nodes": {
                                        "type": "array",
                                        "items": {
                                            "type": "object",
                                            "required": [
                                                "id",
                                                "type",
                                                "address",
                                                "nozzles"
                                            ],
                                            "properties": {
                                                "id": { "type": "string" },
                                                "type": { "type": "string" },
                                                "address": { "type": "integer" },
                                                "nozzles": {
                                                    "type": "array",
                                                    "items": { "type": "integer" }
                                                }
                                            }
                                        }
                                    }
                                }
                            },
                            {
                                "properties":{
                                    "path": { "type": "string" },
                                    "entity": {"const": "topaz"},
                                    "nodes": {
                                        "type": "array",
                                        "items": {
                                            "type": "object",
                                            "required": [
                                                "uid",
                                                "utype",
                                                "uaddress",
                                                "unozzles"
                                            ],
                                            "properties": {
                                                "uid": { "type": "integer" },
                                                "utype": { "type": "string" },
                                                "uaddress": { "type": "string" },
                                                "unozzles": { "type": "boolean" }
                                            }
                                        }
                                    }
                                }
                            }
                        ]
                    },
                    {
                        "properties": {
                            "type": {"const": "io"},
                            "nodes": {
                                "type": "array",
                                "items":{
                                    "type": "object",
                                    "required": [
                                        "num",
                                        "key",
                                        "title",
                                        "path"
                                    ],
                                    "properties": {
                                        "num": { "type": "integer" },
                                        "key": { "type": "integer" },
                                        "title": { "type": "string" },
                                        "path": { "type": "string" }
                                    }
                                }
                            }
                        }
                    }
                ]
            }
        }   
    }
} 
0 голосов
/ 12 ноября 2019

Хотя вложенная anyOf прекрасно работает в JSON Schema, я не думаю, что это лучшее решение в этом случае. Сглаживание anyOf s и использование некоторых definitions очень сильно очищает схему, облегчая рассуждения.

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": ["virtual"],
  "properties": {
    "virtual": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["type"],
        "anyOf": [
          { "$ref": "#/definitions/pdm" },
          { "$ref": "#/definitions/topaz" },
          { "$ref": "#/definitions/io" }
        ]
      }
    }
  },
  "definitions": {
    "pdm": {
      "properties":{
        "type": { "const": "bus" },
        "entity": { "const": "pde" },
        ... type specific properties ...
      }
    },
    "topaz": {
      "properties": {
        "type": { "const": "bus" },
        "entity": { "const": "topaz" },
        ... type specific properties ...
      }
    },
    "io": {
      "properties": {
        "type": { "const": "io" },
        ... type specific properties ...
      }
    }
  }
}

Если вы используете if / then / else или ImplicationШаблон, а не шаблон Enum, позволяет улучшить обмен сообщениями об ошибках, но с более сложной схемой.

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": ["virtual"],
  "properties": {
    "virtual": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["type"],
        "allOf": [
          { "$ref": "#/definitions/pdm" },
          { "$ref": "#/definitions/topaz" },
          { "$ref": "#/definitions/io" }
        ]
      }
    }
  },
  "definitions": {
    "pdm": {
      "if": {
        "properties":{
          "type": { "const": "bus" },
          "entity": { "const": "pde" }
        },
        "required": ["type", "entity"]
      },
      "then": {
        "properties": {
          ... type specific constraints ...
        }
      }
    },
    ... additional types ...
}
...