在本教程的第一部分中,您学习了如何使用所有可用的验证关键字来创建相当高级的架构。 许多JSON数据的实际示例比我们的用户示例更复杂。 试图将所有要求放在这样的数据中的一个文件可能会导致非常大的架构,该架构也可能有很多重复项。
结构化架构
JSON模式的标准允许您将模式分为多个部分。 让我们看一下新闻站点导航的数据示例:
{
"level": 1,
"parent_id": null,
"visitors": "all",
"color": "white",
"pages": [
{
"page_id": 1,
"short_name": "home",
"display_name": "Home",
"url": "/home",
"navigation": {
"level": 2,
"parent_id": 1,
"color": "blue",
"pages": [
{
"page_id": 11,
"short_name": "headlines",
"display_name": "Latest headlines",
"url": "/home/latest",
"navigation": {
"level": 3,
"parent_id": 11,
"color": "white",
"pages": [
{
"page_id": 111,
"short_name": "latest_all",
"display_name": "All",
"url": "/home/latest"
},
...
]
}
},
{
"page_id": 12,
"short_name": "events",
"display_name": "Events",
"url": "/home/events"
}
]
}
},
...
]
}
上面的导航结构与您在http://dailymail.co.uk网站上看到的导航结构有些相似。 您可以在GitHub存储库中看到一个更完整的示例。
数据结构复杂且递归,但是描述此数据的模式非常简单:
navigation.json:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "http://mynet.com/schemas/navigation.json#",
"title": "Navigation",
"definitions": {
"positiveIntOrNull": { "type": ["null", "integer"], "minimum": 1 }
},
"type": "object",
"additionalProperties": false,
"required": [ "level", "parent_id", "color", "pages" ],
"properties": {
"level": { "$ref": "defs.json#/definitions/positiveInteger" },
"parent_id": { "$ref": "#/definitions/positiveIntOrNull" },
"visitors": { "enum": [ "all", "subscribers", "age18" ] },
"color": { "$ref": "defs.json#/definitions/color" },
"pages": {
"type": "array",
"items": { "$ref": "page.json#" }
}
}
}
page.json:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "http://mynet.com/schemas/page.json#",
"title": "Page",
"type": "object",
"additionalProperties": false,
"required": [ "page_id", "short_name", "display_name", "path" ],
"properties": {
"page_id": { "$ref": "defs.json#/definitions/positiveInteger" },
"short_name": { "type": "string", "pattern": "^[a-z_]+$" },
"display_name": { "type": "string", "minLength": 1 },
"path": { "type": "string", "pattern": "^(?:/[a-z_\-]+)+$" },
"color": { "$ref": "defs.json#/definitions/color" },
"navigation": { "$ref": "navigation.json#" }
}
}
defs.json:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "http://mynet.com/schemas/defs.json#",
"title": "Definitions",
"definitions": {
"positiveInteger": { "type": "integer", "minimum": 1 },
"color": {
"anyOf": [
{ "enum": [ "red", "green", "blue", "white" ] },
{ "type": "string", "pattern": "^#(?:(?:[0-9a-fA-F]{1,2})){3}$" }
]
}
}
}
看一下上面的模式和它们描述的导航数据(根据模式navigation.json
有效)。 需要注意的主要事情是,schema navigation.json
引用了page.json
架构,而page.json
又引用了第一个。
用于根据模式验证用户记录JavaScript代码可以是:
var Ajv = require('ajv');
var ajv = Ajv({
allErrors: true,
schemas: [
require('./navigation.json'),
require('./page.json'),
require('./defs.json')
]
});
var validate = ajv.getSchema("http://mynet.com/schemas/navigation.json#");
var valid = validate(navigationData);
if (!valid) console.log(validate.errors);
所有代码示例都可以在GitHub Repository中找到 。
示例中使用的验证器Ajv是JavaScript最快的JSON-Schema验证器。 我创建了它,因此在本教程中将使用它。 最后,我们将研究它与其他验证器的比较方式,以便您为自己选择合适的验证器。
任务
有关如何使用任务安装存储库以及测试答案的说明,请参见本教程的第1部分 。
使用“ $ ref”关键字的模式之间的引用
JSON-Schema标准允许您使用带有“ $ ref”关键字的引用来重复使用模式的重复部分。 从导航示例中可以看到,您可以引用位于的架构:
- 在另一个文件中:使用在其“ id”属性中定义的架构URI
- 在另一个文件的任何部分中:将JSON指针附加到架构引用
- 在当前模式的任何部分中:将JSON指针附加到“#”
您还可以使用等于“#”的“ $ ref”来引用整个当前架构,它允许您创建引用自己的递归架构。
因此,在我们的示例中, navigation.json
的架构是指:
- 模式
page.json
- 模式
defs.json
definitions
- 在同一模式中定义
positiveIntOrNull
page.json
的模式指的是:
- 回到schema
navigation.json
- 也可以
definitions
defs.json
文件中的defs.json
该标准要求“ $ ref”应该是对象中的唯一属性,因此,如果要除了其他模式之外还应用引用的模式,则必须使用“ allOf”关键字。
任务1
使用参考重构本教程第1部分中的用户架构。 将模式分为两个文件: user.json
和connection.json
。
将您的模式放入文件part2/task1/user.json
和part2/task1/connection.json
part2/task1/user.json
part2/task1/connection.json
然后运行node part2/task1/validate
来检查您的模式是否正确。
JSON指针
JSON指针是定义JSON文件各部分路径的标准。 该标准在RFC6901中进行了描述。
该路径由与“ /”字符连接的段(可以是任何字符串)组成。 如果段中包含字符“〜”或“ /”,则应将其替换为“〜0”和“〜1”。 每个段表示JSON数据中的属性或索引。
如果您查看导航示例,则定义color
属性的“ $ ref”是“ defs.json#/ definitions / color”,其中“ defs.json#”是架构URI,“ / definitions / color”是JSON指针。 它指向属性definitions
内的属性color
。
约定是将引用中使用的架构的所有部分放在架构的definitions
属性内(如示例中所示)。 尽管JSON模式用于此目的保留了definitions
关键字,但是并不需要将子计划放在此处。 JSON-pointer允许您引用JSON文件的任何部分。
在URI中使用JSON指针时,应对URI中所有无效的字符进行转义(在JavaScript中,可以使用全局函数encodeURIComponent
)。
不仅可以在JSON模式中使用JSON指针。 它们可用于表示JSON数据中任何属性或项目的路径。 您可以使用库jso