如何对JSON模式进行子类化



如何在JSON模式中进行子类划分

首先,我将自己限制在draft-07中,因为这是我能找到的所有实现。

中描述了进行子分类的天真方法

https://json-schema.org/understanding-json-schema/structuring.html#extending

但这在"additionalProperties"中效果不佳:false?

为什么要麻烦additionalProperties":false?

如果没有它,几乎任何随机垃圾输入json都将被认为是有效的,因为所有"error"(错误的json)将被视为"additionalProperties"。

Recappinghttps://json-schema.org/understanding-json-schema/structuring.html#extending

  • 使用allOf(基类)

  • 然后添加您自己的属性

这个问题是它不适用于"additionalProperties"(因为不清楚但令人遗憾的是,它只适用于额外性质的定义到本地定义的(在该子模式中)属性,因此一个或另一个模式将无法通过验证。

替代方法:

  • JSONSchema之上的元语言/解释器(例如https://github.com/mokkabonna/json-schema-merge-allof)

    这不是一个好的选择,因为scehma只能从javascript(或该元处理器的语言)。而且不容易与其他工具互操作

  • https://github.com/java-json-tools/json-schema-validator/wiki/v5%3A-合并

  • 我将提出一个替代方案作为"解决方案"/回答

我很难做到这一点,尤其是因为我必须使用遗留版本的JSON Schema。我发现这个解决方案有点冗长,但很容易阅读和理解。

比方说,你想描述那种类型:

interface Book {
pageCount: number
}
interface Comic extends Book {
imageCount: number
}
interface Encyclopedia extends Book {
volumeCount: number
}
// This is the schema I want to represent:
type ComicOrEncyclopedia = Comic | Encyclopedia

以下是我如何同时处理多态性并禁止任何额外的道具(同时显然在"子"定义中强制执行继承类型):

{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"bookDefinition": {
"type": "object",
"properties": {
"imageCount": {
"type": "number"
},
"pageCount": {
"type": "number"
},
"volumeCount": {
"type": "number"
}
}
},
"comicDefinition": {
"type": "object",
"allOf": [{ "$ref": "#/definitions/bookDefinition" }],
"properties": {
"imageCount": {},
"pageCount": {},
"volumeCount": {
"not": {}
}
},
"required": ["imageCount", "pageCount"],
"additionalProperties": false
},
"encyclopediaDefinition": {
"type": "object",
"allOf": [{ "$ref": "#/definitions/bookDefinition" }],
"properties": {
"imageCount": {
"not": {}
},
"pageCount": {},
"volumeCount": {}
},
"required": ["pageCount", "volumeCount"],
"additionalProperties": false
}
},
"type": "object",
"oneOf": [
{ "$ref": "#/definitions/comicDefinition" },
{ "$ref": "#/definitions/encyclopediaDefinition" }]
}

这不是一个好答案。但是,在JSONSchema的定义得到改进(或者有人提供了更好的答案)之前,这是我认为可行的。

基本上,您定义每种类型的两个副本,第一个包含所有详细信息,但没有额外的Properties:false标志。然后是第二个,REFERENCING第一个,但使用"additionalProperties:false"集。

第一个你可以认为是"抽象类",第二个你可以想象成"具体类"。

然后,对于"subclass",您使用https://json-schema.org/understanding-json-schema/structuring.html#extending方法,但引用了抽象类,然后添加"additionalProperties:false"。可悲的是,为了实现这一点,您还必须重复所有继承的属性(但不需要包括它们的类型信息,只需要它们的名称),因为JSONSchema草案7似乎对解释additionalProperties的方式做出了可悲的选择。

示例-基于https://json-schema.org/understanding-json-schema/structuring.html#extending应该有帮助:

https://www.jsonschemavalidator.net/s/3fhU3O1X

(此处转载,以防其他网站/链路不可持久/可靠)

{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://TEST",
"definitions": {
"interface-address": {
"type": "object",
"properties": {
"street_address": {
"type": "string"
},
"city": {
"type": "string"
},
"state": {
"type": "string"
}
},
"required": ["street_address", "city", "state"]
},
"concrete-address": {
"allOf": [
{
"$ref": "#/definitions/interface-address"
}
],
"properties": {
"street_address": {},
"city": {},
"state": {}
},
"additionalProperties": false
},
"in-another-file-subclass-address": {
"allOf": [
{
"$ref": "#/definitions/interface-address"
}
],
"additionalProperties": false,
"properties": {
"street_address": {},
"city": {},
"state": {},
"type": {
"enum": ["residential", "business"]
}
},
"required": ["type"]
},
"test-of-address-schemas": {
"type": "object",
"properties": {
"interface-address-allows-bad-fields": {
"$ref": "#/definitions/interface-address"
},
"use-concrete-address-to-only-admit-legit-addresses-without-extra-crap": {
"$ref": "#/definitions/concrete-address"
},
"still-can-subclass-using-interface-not-concrete": {
"$ref": "#/definitions/in-another-file-subclass-address"
}
}
}
},
"anyOf": [
{
"$ref": "#/definitions/test-of-address-schemas"
}
]
}

和示例文档:

{
"interface-address-allows-bad-fields":{
"street_address":"s",
"city":"s",
"state":"s",
"allow-bad-fields-this-is-why-we-need-additionalProperties":"s"
},
"use-concrete-address-to-only-admit-legit-addresses-without-extra-crap":{
"street_address":"s",
"city":"s",
"state":"s"
},
"still-can-subclass-using-interface-not-concrete":{
"street_address":"s",
"city":"s",
"state":"s",
"type":"business"
}
}

如何在JSON模式中进行子类划分?

您没有,因为JSON模式不是面向对象的,模式也不是类。JSON模式是为验证而设计的。架构是约束的集合。

但是,无论如何,让我们从OO的角度来看待它。

组合而非继承

首先要注意的是,JSONSchema不支持对继承的模拟。你可能熟悉OO的古老智慧;组合而非继承";。Go语言选择完全不支持继承,因此JSONSchema非常适合这种方法。如果您只使用composition构建系统,那么"additionalProperties": false就没有问题。

多态性

比方说,从构图的角度思考太过陌生(需要时间才能学会以不同的方式思考),或者你无法控制你的类型是如何设计的。无论出于何种原因,您都需要使用继承对数据进行建模,您可以使用熟悉的allOf模式。allOf模式与继承模式不同,但它是最接近的模式。

正如您所指出的,"additionalProperties": falseallOf模式一起造成严重破坏。那么,你为什么要忽略这个呢?OO的答案是多态性。假设你有一个";"人";类型和一个";学生;扩展";人";。如果你有一个Student,你应该能够将它传递给一个接受Person的方法。Student有一些Person没有的属性并不重要,当它被用作Person时,额外的属性会被忽略。如果使用"additionalProperties": false,则类型不可能是多态的。


这些都不是您所要求的解决方案,但希望它能给您一个不同的视角,让您考虑以更适合JSON模式的不同方式解决问题的替代方案。

最新更新