如何将 oneOf 与通用子模式 (V4) 一起使用

How to use oneOf alongside a common sub-schema (V4)

我在尝试创建同时使用 oneOf 和公共引用子模式的模式时遇到问题,以避免必须复制模式的某些部分。 架构应验证的 JSON 如下所示:

{
  "createdDate": "2015-01-20T17:10:05Z",
  "createdBy": "testUser",
  "lastModifiedDate": "2015-01-20T17:10:05Z",
  "lastModifiedBy": "testUser",
  "fileUrl": {
    "path": "/path/to/file",
    "fileName": "file.pdf"
  },
  "referenceType": "person",
  "fileType": "certificate",
  "personId": "12345"
}

据此,共同的部分是:

{
  "createdDate": "2015-01-20T17:10:05Z",
  "createdBy": "testUser",
  "lastModifiedDate": "2015-01-20T17:10:05Z",
  "lastModifiedBy": "testUser",
  "fileUrl": {
    "path": "/path/to/file",
    "fileName": "file.pdf"
  }
}

其余 3 个字段的名称始终相同且均为必填字段,但它们允许的枚举值会有所不同。

所以其余 3 个的架构可能是以下之一:

{
"properties": {
    "referenceType": {
        "type": "string",
        "enum": [
            "vehicle"
        ]
    },
    "fileType": {
        "type": "string",
        "enum": [
            "registration document"
        ]
    },
    "vehicleId": {
        "type": "string",
        "pattern": "[^ ]"
    }
},
"required": [
    "vehicleId"
]
}

{
    "properties": {
        "referenceType": {
            "type": "string",
            "enum": [
                "person"
            ]
        },
        "fileType": {
            "type": "string",
            "enum": [
                "certificate"
            ]
        },
        "personId": {
            "type": "string",
            "pattern": "[^ ]"
        }
    },
    "required": [
        "personId"
    ]
}

我似乎无法创建一个模式,从而避免重复公共字段,并拥有一个,并在整个模式中将 additionalProperties 设置为 false。 在下面的示例中,尝试将 additionalProperties 设置为 false 会导致验证错误。可以做我想做的事吗?

{
    "type": "object",
    "$schema": "http://json-schema.org/draft-04/schema#",
    "definitions": {
        "commonFile": {
            "properties": {
                "createdDate": {
                    "type": "string",
                    "format": "date-time"
                },
                "createdBy": {
                    "type": "string",
                    "pattern": "[^ ]"
                },
                "lastModifiedDate": {
                    "type": "string",
                    "format": "date-time"
                },
                "lastModifiedBy": {
                    "type": "string",
                    "pattern": "[^ ]"
                },
                "fileUrl": {
                    "type": "object",
                    "additionalProperties": false,
                    "properties": {
                        "path": {
                            "type": "string",
                            "pattern": "[^ ]"
                        },
                        "fileName": {
                            "type": "string",
                            "pattern": "[^ ]"
                        }
                    },
                    "required": [
                        "path",
                        "fileName"
                    ]
                }
            }   
        }
    },
    "oneOf": [{
            "allOf": [
                {"$ref": "#/definitions/commonFile"}, 
                {
                    "properties": {
                        "referenceType": {
                            "type": "string",
                            "enum": [
                                "person"
                            ]
                        },
                        "fileType": {
                            "type": "string",
                            "enum": [
                                "certificate"
                            ]
                        },
                        "personId": {
                            "type": "string",
                            "pattern": "[^ ]"
                        }
                    },
                    "required": [
                        "personId"
                    ]
                }
            ]
        }, {
            "allOf": [
                {"$ref": "#/definitions/commonFile"}, 
                {
                    "properties": {
                        "referenceType": {
                            "type": "string",
                            "enum": [
                                "vehicle"
                            ]
                        },
                        "fileType": {
                            "type": "string",
                            "enum": [
                                "registration document"
                            ]
                        },
                        "vehicleId": {
                            "type": "string",
                            "pattern": "[^ ]"
                        }
                    },
                    "required": [
                        "vehicleId"
                    ]
                }
            ]
        }
    ],
    "required": [
        "createdDate",
        "createdBy",
        "lastModifiedDate",
        "lastModifiedBy",
        "fileUrl",
        "referenceType",
        "fileType"
    ]
}

我建议不要additionalProperties 设置为 false。通常最好只是安静地忽略未定义的属性。以下架构是您为实现目标而必须执行的操作。

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "createdDate": { "type": "string", "format": "date-time" },
    "createdBy": { "type": "string", "pattern": "[^ ]" },
    "lastModifiedDate": { "type": "string", "format": "date-time" },
    "lastModifiedBy": { "type": "string", "pattern": "[^ ]" },
    "fileUrl": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "path": { "type": "string", "pattern": "[^ ]" },
        "fileName": { "type": "string", "pattern": "[^ ]" }
      },
      "required": ["path", "fileName"]
    },
    "referenceType": { "type": "string" },
    "fileType": { "type": "string" },
    "personId": {},
    "vehicleId": {}
  },
  "additionalProperties": false,
  "anyOf": [
    {
      "properties": {
        "referenceType": { "enum": ["person"] },
        "fileType": { "enum": ["certificate"] },
        "personId": { "type": "string", "pattern": "[^ ]" }
      },
      "required": ["personId"],
      "not" : { "required": ["vehicleId"] }
    },
    {
      "properties": {
        "referenceType": { "enum": ["vehicle"] },
        "fileType": { "enum": ["registration document"] },
        "vehicleId": { "type": "string", "pattern": "[^ ]" }
      },
      "required": ["vehicleId"],
      "not" : { "required": ["personId"] }
    }
  ],
  "required": ["createdDate", "createdBy", "lastModifiedDate", "lastModifiedBy", "fileUrl", "referenceType", "fileType"]
}

我做的第一件事是删除所有无关的 allOf。只需要一个anyOf。我在主模式中定义了公共属性。 anyOf 子句中描述了变体。

如果将 additionalProperties 设置为 false,则附加架构(例如 allOfoneOfanyOf)无法引入新属性.这意味着该架构中允许的所有属性都必须包含在声明 additionalPropertiesfalse 的架构中。这就是我在主模式中声明 referenceTypefileTypepersonIdvechicleId 的原因。

现在的问题是当referenceTypepersonadditionalProperties不再排除vehicleId或当referenceType为[=]时排除personId 33=]。为了确保不允许这样做,我添加了 not 子句。

如果您添加第三个变体,限制其他属性会变得更加困难。它不仅仅是向 anyof 架构数组添加一个额外的 referenceType。您还必须向主模式添加新属性,并禁止现有 referenceTypesall 的这些新类型。您必须对整个架构进行更改,而不仅仅是正在更改的区域。这就是为什么将 additionalProperties 设置为 false 通常不是最好的主意。