互联网工程任务组 A. Wright,编辑
互联网草案
预期状态:信息性 H. Andrews,编辑
过期日期:2021 年 8 月 1 日
B. Hutton,编辑
G. Dennis
2020 年 1 月 28 日

JSON Schema: 用于描述 JSON 文档的媒体类型
draft-bhutton-json-schema-00

摘要

JSON Schema 定义了媒体类型 "application/schema+json",一种基于 JSON 的格式,用于描述 JSON 数据的结构。JSON Schema 声明 JSON 文档必须具备的格式,提取信息的方式以及与之交互的方式。媒体类型 "application/schema-instance+json" 提供了比 "application/json" 文档所能提供的更丰富的功能,并与 "application/schema+json" 进行了集成。

致读者

此草案的议题列表可在 <https://github.com/json-schema-org/json-schema-spec/issues> 找到。

有关更多信息,请参见 <https://json-schema.fullstack.org.cn/>

要提供反馈,请使用此议题跟踪器,主页上列出的通信方法,或通过电子邮件与文档编辑人员联系。

本文档状态

此互联网草案完全符合 BCP 78 和 BCP 79 的规定。

互联网草案是互联网工程任务组 (IETF) 的工作文档。请注意,其他组也可能以互联网草案的形式发布工作文档。当前互联网草案的列表位于 https://datatracker.ietf.org/drafts/current/。

互联网草案是有效期最长为六个月的草案文件,可能随时更新、替换或被其他文档废弃。不适合将互联网草案用作参考材料,或将其引用为除“正在进行中的工作”以外的其他内容。

此互联网草案将于 2021 年 8 月 1 日过期。

版权声明

版权所有 (c) 2020 IETF 信托和被识别为文档作者的人员。保留所有权利。

本文件受 BCP 78 和 IETF 信托的《与 IETF 文档相关的法律条款》(https://trustee.ietf.org/license-info)约束,这些条款在发布本文件之日起生效。请仔细阅读这些文件,因为它们描述了您对本文件的权利和限制。从本文件中提取的代码组件必须包含简化的 BSD 许可证文本(如信托法律条款第 4.e 节所述),并且按简化的 BSD 许可证的描述提供,不提供任何担保。


目录

1. 简介

JSON Schema 是一种用于定义 JSON 数据结构的 JSON 媒体类型。JSON Schema 旨在定义 JSON 数据的验证、文档、超链接导航和交互控制。

本规范定义了 JSON Schema 核心术语和机制,包括通过引用指向另一个 JSON Schema、反引用 JSON Schema 引用、指定使用的方言、指定方言的词汇表要求以及定义预期的输出。

其他规范定义了用于对验证、链接、注释、导航和交互进行断言的词汇表。

2. 约定和术语

本文件中的关键词“必须”、“禁止”、“需要”、“应”、“不应”、“建议”、“不建议”、“推荐”、“可以”和“可选”的解释方式与 RFC 2119 中描述的相同。

本文件中的术语“JSON”、“JSON 文本”、“JSON 值”、“成员”、“元素”、“对象”、“数组”、“数字”、“字符串”、“布尔值”、“真”、“假”和“空”的解释方式与 RFC 8259 中定义的相同。

3. 概述

本文件提出了一种新的媒体类型“application/schema+json”,用于标识用于描述 JSON 数据的 JSON Schema。它还提出了一种可选的媒体类型“application/schema-instance+json”,以提供额外的集成功能。JSON Schema 本身就是 JSON 文档。本规范以及相关规范定义了关键字,允许作者以多种方式描述 JSON 数据。

JSON Schema 使用关键字对 JSON 实例进行约束,或用其他信息对其进行注释。其他关键字用于将断言和注释应用于更复杂的 JSON 数据结构,或基于某种条件进行应用。

为了便于重用,关键字可以组织成词汇表。词汇表包含一个关键字列表,以及它们的语法和语义。方言被定义为一组词汇表以及在元 Schema 中识别的所需支持。

JSON Schema 可以通过定义额外的词汇表来扩展,或者非正式地通过在任何词汇表之外定义额外的关键字来扩展。无法识别的单个关键字只将其值作为注释收集,而对于无法识别的词汇表,其行为可以在声明使用哪些词汇表时进行控制。

本规范定义了一个核心词汇表,任何实现都必须支持该词汇表,并且无法禁用它。它的每个关键字都以“$”字符作为前缀,以强调其必需的性质。此词汇表对于“application/schema+json”媒体类型的正常运行至关重要,并用于引导加载其他词汇表。

此外,本文档定义了一个用于有条件地应用子模式以及将子模式应用于对象和数组内容的**推荐**关键字词汇表。无论这些模式是用于断言验证、注释还是两者兼顾,都需要使用此词汇表或类似的词汇表来编写非平凡 JSON 实例的模式。虽然不是必需的核心词汇表的一部分,但为了最大限度地实现互操作性,本文档中包含了此附加词汇表,并强烈建议使用它。

用于结构验证或超媒体注释等目的的进一步词汇表在其他文档中定义。这些其他文档都定义了一个方言,该方言收集了编写针对该文档目的的模式所需的标准词汇表集。

4. 定义

4.1. JSON 文档

JSON 文档是应用/json 媒体类型描述的信息资源(一系列八位字节)。

在 JSON Schema 中,由于它定义的数据模型,“JSON 文档”、“JSON 文本”和“JSON 值”这几个术语可以互换使用。

JSON Schema 仅在 JSON 文档上定义。但是,任何可以解析成或根据 JSON Schema 数据模型处理的文档或内存结构都可以针对 JSON Schema 进行解释,包括 CBOR 等媒体类型。

4.2. 实例

应用模式的 JSON 文档称为“实例”。

JSON Schema 定义在 “application/json” 或兼容文档上,包括具有 “+json” 结构语法后缀的媒体类型。

其中,本规范定义了 “application/schema-instance+json” 媒体类型,该类型定义了对 URI 中片段的处理。

4.2.1. 实例数据模型

JSON Schema 根据数据模型解释文档。根据此数据模型解释的 JSON 值称为“实例”。

实例具有六种基本类型之一,并且根据类型具有一系列可能的值

null
JSON “null” 值
boolean
来自 JSON “true” 或 “false” 值的 “true” 或 “false” 值
object
来自 JSON “object” 值的属性无序集,将字符串映射到实例
array
来自 JSON “array” 值的实例有序列表
number
来自 JSON “number” 值的任意精度、十进制基数数字值
string
来自 JSON “string” 值的 Unicode 代码点字符串

因此,空白和格式问题(包括数字在数据模型中相等的不同词法表示形式)不在 JSON Schema 的范围内。希望处理此类词法表示形式差异的 JSON Schema 词汇表 应定义关键字以精确地解释数据模型中的格式化字符串,而不是依赖于原始 JSON 表示形式的 Unicode 字符可用。

由于对象不能具有两个具有相同键的属性,因此对于尝试在一个对象中定义两个具有相同键的属性的 JSON 文档的行为是未定义的。

请注意,JSON Schema 词汇表可以自由定义自己的扩展类型系统。这不应与此处定义的核心数据模型类型混淆。例如,“integer” 是词汇表可以定义为关键字值的合理类型,但数据模型没有区分整数和其他数字。

4.2.2. 实例相等性

当且仅当两个 JSON 实例具有相同的类型,并且根据数据模型具有相同的值时,它们被称为相等。具体来说,这意味着

此定义隐含数组必须具有相同的长度,对象必须具有相同数量的成员,对象中的属性是无序的,没有办法定义多个具有相同键的属性,并且仅仅的格式差异(缩进、逗号的位置、尾随零)是不重要的。

4.2.3. 非 JSON 实例

可以使用 JSON Schema 与 JSON Schema 数据模型的超集,其中实例可能不在六种 JSON 数据类型中的任何一种。

在这种情况下,注释仍然适用;但大多数验证关键字将无用,因为它们总是通过或总是失败。

自定义词汇表可以定义对核心数据模型超集的支持。模式本身可能只能在此超集中表达;例如,要使用 “const” 关键字。

4.3. JSON Schema 文档

JSON Schema 文档,或简称为模式,是一个用于描述实例的 JSON 文档。模式本身可以解释为实例,但始终应使用媒体类型 “application/schema+json” 而不是 “application/schema-instance+json”。定义 “application/schema+json” 媒体类型是为了提供 “application/schema-instance+json” 提供的片段标识符语法和语义的超集。

JSON Schema 必须是对象或布尔值。

4.3.1. JSON Schema 对象和关键字

应用于实例的对象属性称为关键字或模式关键字。从广义上讲,关键字分为五类之一

标识符
通过设置模式的规范 URI 和/或更改基本 URI 的确定方式来控制模式标识
断言
应用于实例时产生布尔结果
注释
将信息附加到实例以供应用程序使用
应用器
将一个或多个子模式应用于实例中的特定位置,并组合或修改其结果
保留位置
不直接影响结果,但保留一个用于特定目的的位置以确保互操作性

关键字可能属于多个类别,尽管应用器应该只根据其子模式的结果生成断言结果。它们不应定义与其子模式无关的附加约束。

同一模式对象内的属性称为相邻关键字。

扩展关键字,即本文档及其配套文档之外定义的关键字,可以自由定义其他行为。

JSON Schema 可以包含不是模式关键字的属性。未知关键字应被视为注释,其中关键字的值是注释的值。

空模式是没有任何属性或只有未知属性的 JSON Schema。

4.3.2. 布尔 JSON 模式

布尔模式值 “true” 和 “false” 是始终产生自身作为断言结果的平凡模式,与实例值无关。它们永远不会生成注释结果。

这些布尔模式的存在是为了阐明模式作者的意图并促进模式处理优化。它们的执行方式与以下模式对象相同(其中 “not” 是本文档中定义的子模式应用程序词汇表的一部分)。

true
始终通过验证,就像空模式 {} 一样
false
始终无法通过验证,就像模式 { "not": {} } 一样

虽然空模式对象是明确的,但 “false” 模式有很多可能的等效项。使用布尔值确保对人类读者和实现者来说意图是明确的。

4.3.3. 模式词汇表

模式词汇表,或简称为词汇表,是一组关键字、它们的语法及其语义。词汇表通常围绕特定目的进行组织。JSON Schema 的不同用途,例如验证、超媒体或用户界面生成,将涉及不同的词汇表集。

词汇表是 JSON Schema 中的主要重复使用单位,因为模式作者可以指示处理模式所需的或可选的词汇表。由于词汇表在元模式中通过 URI 标识,因此通用实现可以加载扩展以支持以前未知的词汇表。虽然关键字可以在任何词汇表之外被支持,但没有类似的机制来指示单个关键字的使用情况。

4.3.4. 元模式

自身描述模式的模式称为元模式。元模式用于验证 JSON 模式并指定它们使用的词汇表。

通常,元模式会指定一组词汇表,并验证符合这些词汇表语法的模式。但是,元模式和词汇表是分开的,以便元模式可以比词汇表规范要求的更严格或更宽松地验证模式一致性。元模式还可以描述和验证不是正式词汇表一部分的附加关键字。

4.3.5. 根模式、子模式和资源

JSON Schema 资源是通过 规范绝对 URI 识别的模式。

根模式是构成所讨论的整个 JSON 文档的模式。根模式始终是模式资源,其中 URI 按照第 9.1.1 节所述确定。

一些关键字本身就采用模式,允许 JSON 模式嵌套

{
    "title": "root",
    "items": {
        "title": "array item"
    }
}

                        

在此示例文档中,标题为 “array item” 的模式是子模式,标题为 “root” 的模式是根模式。

与根模式一样,子模式要么是对象,要么是布尔值。

如第 8.2.1 节所述,JSON Schema 文档可以包含多个 JSON Schema 资源。在没有限定的情况下使用时,术语 “根模式” 指的是文档的根模式。在某些情况下,会讨论资源根模式。资源的根模式是其顶级模式对象,如果资源要提取到独立的 JSON Schema 文档中,它也将是文档根模式。

无论多个模式资源是嵌入还是使用引用链接,它们都以相同的方式处理,具有相同的可用行为。

5. 片段标识符

根据 RFC 6839 的第 3.1 节,为任何 +json 媒体类型指定的片段标识符语法和语义应与 “application/json” 指定的相同。(在本规范发布时,没有为 “application/json” 定义片段标识语法。)

此外,“application/schema+json” 媒体类型支持两种片段标识符结构:普通名称和 JSON 指针。“application/schema-instance+json” 媒体类型支持一种片段标识符结构:JSON 指针。

RFC 6901 中描述了将 JSON 指针用作 URI 片段标识符。对于支持两种片段标识符语法的 “application/schema+json”,必须将匹配 JSON 指针语法的片段标识符(包括空字符串)解释为 JSON 指针片段标识符。

根据 W3C 的 片段标识符最佳实践,“application/schema+json” 中的普通名称片段标识符保留用于引用本地命名的模式。所有与 JSON 指针语法不匹配的片段标识符必须解释为普通名称片段标识符。

在 “application/schema+json” 文档中定义和引用普通名称片段标识符在 “$anchor” 关键字 部分中指定。

6. 一般注意事项

6.1. JSON 值范围

实例可以是 JSON 定义的任何有效 JSON 值。JSON Schema 不对类型施加任何限制:JSON Schema 可以描述任何 JSON 值,包括例如 null。

6.2. 编程语言无关性

JSON Schema 与编程语言无关,并支持数据模型中描述的所有值范围。但是,请注意,某些语言和 JSON 解析器可能无法在内存中表示 JSON 可描述的所有值范围。

6.3. 数学整数

某些编程语言和解析器对浮点数使用与整数不同的内部表示。

为了保持一致性,整数 JSON 数字不应使用小数部分编码。

6.4. 正则表达式

关键字可以使用正则表达式来表达约束,或将实例值约束为正则表达式。这些正则表达式应根据 ECMA-262,第 21.2.1 节 中描述的正则表达式方言有效。

正则表达式应使用“u”标志(或等效标志)构建以提供 Unicode 支持,或以提供 Unicode 支持的方式进行处理,如 ECMA-262 中定义的那样。

此外,鉴于正则表达式构造支持的差异很大,模式作者应将自己限制在以下正则表达式标记中

最后,实现不得将正则表达式视为固定,无论是在开头还是在结尾。这意味着,例如,模式“es”匹配“expression”。

6.5. 扩展 JSON Schema

任何实体都可以定义其他模式关键字和模式词汇表。除了明确协议外,模式作者不应期望这些额外的关键字和词汇表得到没有明确记录此类支持的实现的支持。实现应将它们不支持的关键字视为注释,其中关键字的值是注释的值。

实现可以提供注册或加载处理程序的功能,以用于它们不支持的词汇表。注册和实现此类处理程序的确切机制取决于实现。

7. 关键字行为

JSON Schema 关键字属于几个通用行为类别。断言验证实例是否满足约束,产生布尔结果。注释附加信息,应用程序可以根据需要以任何方式使用这些信息。应用器将子模式应用于实例的各个部分,并组合它们的结果。

扩展关键字应保持在这些类别中,记住注释的灵活性和特殊性。复杂的行为通常最好委托给应用程序,以便在注释数据的基础上执行,而不是直接作为模式关键字实现。但是,扩展关键字可以为特定目的定义其他行为。

根据模式评估实例涉及对实例中适当的位置处理模式中的所有关键字。通常,应用器关键字会一直被处理,直到遇到没有应用器(因此没有子模式)的模式对象。实例中的适当位置将根据模式对象中的断言和注释关键字进行评估,并且它们的结果将根据应用器的规则收集到父模式中。

当所有子模式都已评估时,父模式对象的评估可以完成,尽管在某些情况下,由于断言结果,评估可能会被短路。当收集注释时,某些断言结果短路是不可能的,因为需要检查所有子模式以收集注释,包括那些无法进一步更改断言结果的子模式。

7.1. 词法范围和动态范围

虽然大多数 JSON Schema 关键字可以单独评估,或者最多需要考虑相同模式对象中相邻关键字的值或结果,但有些关键字的行为更复杂。

关键字的词法范围由对象和数组的嵌套 JSON 数据结构决定。最大的范围是整个模式文档。最小的范围是单个没有子模式的模式对象。

关键字可以使用部分值(例如 URI 引用)来定义,该值必须针对另一个值(例如另一个 URI 引用或完整 URI)解析,该值是通过 JSON 文档的词法结构找到的。"$id"、"$ref" 和 "$dynamicRef" 核心关键字,以及 "base" JSON 超模式关键字,是此类行为的示例。

请注意,某些关键字,例如 "$schema",适用于整个模式资源的词法范围,因此必须只出现在模式资源的根模式中。

其他关键字可能会考虑在评估模式期间存在的动态范围,通常与实例文档一起使用。最外层的动态范围是处理开始的模式对象,即使它不是模式资源根。从这个根模式到任何特定关键字(包括可能已解析的任何 "$ref" 和 "$dynamicRef" 关键字)的路径被认为是关键字的“验证路径”。

词法范围和动态范围在遇到引用关键字之前是一致的。虽然遵循引用关键字会将处理从一个词法范围移动到另一个词法范围,但从动态范围的角度来看,遵循引用与下降到作为值的子模式没有什么不同。在该引用另一侧的关键字通过动态范围解析信息时,将考虑引用的起始侧作为其动态父级,而不是检查本地词法封闭的父级。

动态范围的概念主要用于 "$dynamicRef" 和 "$dynamicAnchor",应将其视为高级功能,并在定义其他关键字时谨慎使用。它还出现在报告错误和收集的注释时,因为它可能多次使用不同的动态范围重新访问相同的词法范围。在这种情况下,重要的是向用户通知生成错误或注释的动态路径。

7.2. 关键字交互

关键字行为可以根据 子模式 和/或相邻关键字(相同模式对象中的关键字)及其子模式的注释结果来定义。此类关键字不得导致循环依赖关系。关键字可以根据相同 模式对象 中是否存在其他关键字来修改其行为。

7.3. 默认行为

缺少的关键字不得产生错误的断言结果,不得产生注释结果,也不得导致任何其他模式作为其自身行为定义的一部分进行评估。但是,鉴于缺少的关键字不会产生注释,注释结果的缺乏可能会间接改变其他关键字的行为。

在某些情况下,缺少关键字断言行为与特定值产生的行为相同,关键字定义应在已知的情况下记录此类值。但是,即使产生默认行为的值会在存在时产生注释结果,默认行为仍然不得导致注释。

由于注释收集会增加计算和内存方面的显著成本,因此实现可以选择退出此功能。根据收集的注释指定的关键字应在适当的情况下描述合理的替代方法。本文件中的 "items" 和 "additionalProperties" 关键字演示了这种方法。

请注意,当某个关键字不可能使用这种替代方法时,不支持注释收集的实现将无法支持包含这些关键字或词汇表的那些关键字或词汇表。

7.4. 标识符

标识符设置模式的规范 URI,或影响在 引用 中解析此类 URI 的方式,或两者兼而有之。本文件定义的核心词汇表定义了几个标识关键字,最显著的是 "$id"。

规范模式 URI 在处理实例期间不得更改,但影响 URI 引用解析的关键字的行为可能只有在运行时才能完全确定。

虽然自定义标识符关键字是可能的,但词汇表设计者应该注意不要破坏核心关键字的功能。例如,本规范中的 "$dynamicAnchor" 关键字将其实现 URI 解析效果限制在匹配的 "$dynamicRef" 关键字中,使 "$ref" 的行为不受干扰。

7.5. 应用器

应用器允许构建比使用单个模式对象所能实现的更复杂的模式。根据 模式文档 评估实例从将 根模式 应用于完整的实例文档开始。从那里,称为应用器的关键字用于确定应用哪些其他模式。此类模式可以就地应用于当前位置,也可以应用于子位置。

要应用的模式可以作为构成关键字值全部或部分的子模式存在。或者,应用器可以引用相同模式文档中或不同模式文档中的其他地方的模式。标识此类引用模式的机制由关键字定义。

应用器关键字还定义了如何修改和/或组合子模式或引用模式的布尔 断言 结果,以产生应用器的布尔结果。应用器可以对子模式的断言结果应用任何布尔逻辑运算,但不得引入自己的新断言条件。

注释 结果将与实例位置和模式关键字的位置一起保留,以便应用程序可以决定如何解释多个值。

7.5.1. 引用模式和引用模式

第 7.5 节 所述,应用器关键字可以引用要应用的模式,而不是将其作为子模式包含在应用器的值中。在这种情况下,被应用的模式被称为引用模式,而包含应用器关键字的模式被称为引用模式。

虽然根模式和子模式是基于模式在模式文档中的位置的静态概念,但引用模式和引用模式是动态的。在根据模式评估实例期间,不同的模式对可能会发现自己处于各种引用和引用安排中。

对于某些按引用应用器,例如 "$ref",引用的模式可以通过对模式文档的词法范围进行静态分析来确定。其他应用器,例如 "$dynamicRef"(使用 "$dynamicAnchor"),可能使用动态作用域,因此只有在使用实例评估模式时才能解析。

7.6. 断言

JSON Schema 可以用来断言 JSON 文档的约束,这些约束要么通过断言,要么失败。这种方法可以用来验证是否符合约束,或者记录满足约束所需的内容。

JSON Schema 实现会在将实例与模式断言进行评估时产生单个布尔结果。

实例只能在模式中存在的断言中失败。

7.6.1. 断言和实例基本类型

大多数断言只约束特定基本类型中的值。当实例的类型不是关键字目标类型的类型时,该实例被认为符合断言。

例如,来自配套 验证词汇表 的 "maxLength" 关键字:只会限制某些(过长的)字符串成为有效字符串。如果实例是数字、布尔值、空值、数组或对象,那么它对于该断言是有效的。

这种行为允许关键字更容易地与可以具有多种基本类型的实例一起使用。配套验证词汇表还包含一个 "type" 关键字,它可以独立地将实例限制为一种或多种基本类型。这允许以简洁的方式表达用例,例如一个可能返回特定长度的字符串或空值的函数。

{
    "type": ["string", "null"],
    "maxLength": 255
}

                        

如果 "maxLength" 还将实例类型限制为字符串,那么这将非常难以表达,因为按写法示例实际上不允许空值。除非明确指定,否则每个关键字都是单独评估的,因此如果 "maxLength" 将实例限制为字符串,那么在 "type" 中包含 "null" 将不会有任何有用的效果。

7.7. 注释

JSON Schema 可以使用信息对实例进行注释,无论实例是否通过包含注释的模式对象及其所有父模式对象进行验证。该信息可以是一个简单值,也可以根据实例内容计算得出。

注释附加到实例中的特定位置。由于许多子模式可以应用于任何单个位置,因此应用程序可能需要决定如何处理由同一模式对象中同一模式关键字在不同模式对象中附加到同一实例位置的不同注释值。

与断言结果不同,注释数据可以采用各种形式,这些形式提供给应用程序以供其自行使用。JSON Schema 实现不需要代表应用程序使用收集的信息。

除非另有说明,否则注释关键字的值为关键字的值。但是,其他行为也是可能的。例如,JSON 超级模式 的 "links" 关键字是一个复杂的注释,它基于部分实例数据生成一个值。

虽然断言可以进行 "短路" 评估,但收集注释需要检查应用于实例位置的所有模式,即使它们不能改变整体断言结果。唯一的例外是,对于已失败验证的模式对象的子模式,可以跳过它们,因为不会为失败的模式保留注释。

7.7.1. 收集注释

注释由明确定义注释收集行为的关键字收集。请注意,布尔模式无法生成注释,因为它们不使用关键字。

收集的注释必须包含以下信息

7.7.1.1. 区分多个值

应用程序可以根据贡献值的模式位置决定使用多个注释值中的哪一个。这是为了允许灵活使用。收集模式位置有利于这种使用。

例如,考虑这个模式,它使用来自 验证规范 的注释和断言

请注意,某些行是为了清晰而换行的。

{
    "title": "Feature list",
    "type": "array",
    "prefixItems": [
        {
            "title": "Feature A",
            "properties": {
                "enabled": {
                    "$ref": "#/$defs/enabledToggle",
                    "default": true
                }
            }
        },
        {
            "title": "Feature B",
            "properties": {
                "enabled": {
                    "description": "If set to null, Feature B
                                    inherits the enabled
                                    value from Feature A",
                    "$ref": "#/$defs/enabledToggle"
                }
            }
        }
    ],
    "$defs": {
        "enabledToggle": {
            "title": "Enabled",
            "description": "Whether the feature is enabled (true),
                            disabled (false), or under
                            automatic control (null)",
            "type": ["boolean", "null"],
            "default": null
        }
    }
}

                            

在这个例子中,功能 A 和功能 B 都使用了可重用的 "enabledToggle" 模式。该模式使用 "title"、"description" 和 "default" 注释。因此,应用程序必须决定如何处理功能 A 的额外 "default" 值和功能 B 的额外 "description" 值。

应用程序程序员和模式作者需要就用法达成一致。对于这个例子,让我们假设他们同意使用最具体的 "default" 值,任何额外的、更通用的 "default" 值将被静默忽略。让我们还假设他们同意使用所有 "description" 文本,从最通用的开始,以最具体的结束。这要求模式作者编写在这样组合时有效的描述。

应用程序可以使用模式位置路径来确定哪些值是哪些。特征的直接 "enabled" 属性模式中的值更具体,而引用到 "$ref" 的可重用模式下的值更通用。模式位置路径将显示每个值是否通过 "$ref" 找到。

因此,功能 A 将使用默认值为 true,而功能 B 将使用通用默认值为 null。功能 A 只有来自 "enabledToggle" 模式中的通用描述,而功能 B 将使用该描述,并附加其本地定义的描述,说明如何解释空值。

请注意,不同的应用程序可能采取其他合理的方法。例如,应用程序可能会认为 "default" 的两个不同值的存在是错误,无论它们的模式位置如何。

7.7.1.2. 注释和断言

产生 false 断言结果的模式对象一定不能产生任何注释结果,无论是来自它们自己的关键字,还是来自子模式中的关键字。

请注意,总体模式结果可能仍然包含从其他模式位置收集的注释。给定此模式

{
    "oneOf": [
        {
            "title": "Integer Value",
            "type": "integer"
        },
        {
            "title": "String Value",
            "type": "string"
        }
    ]
}

                            

针对实例 "This is a string",标题注释 "Integer Value" 被丢弃,因为该模式对象中的类型断言失败。标题注释 "String Value" 被保留,因为该实例通过了字符串类型断言。

7.7.1.3. 注释和应用器

除了可能定义自己的注释结果外,应用器关键字还聚合在其子模式(s)或引用模式(s)中收集的注释。

7.8. 保留位置

第四类关键字只是保留一个位置来保存模式作者感兴趣的可重用组件或数据,这些组件或数据不适合重用。这些关键字不影响验证或注释结果。它们在核心词汇表中的目的是确保位置可用于某些目的,并且不会被扩展关键字重新定义。

虽然这些关键字不会直接影响结果,但正如第 9.4.2 节所述,保留可重用模式位置的未识别扩展关键字在某些情况下可能与引用有不良互动。

7.9. 加载实例数据

虽然作为本文件或相关文档一部分定义的词汇表都没有定义可能针对和/或加载实例数据的关键字,但其他词汇表可能希望这样做。

关键字可以使用 JSON 指针或相对 JSON 指针来检查当前评估位置之外的实例部分。

允许使用相对 JSON 指针调整位置的关键字应该在需要默认值时默认使用当前位置。

8. JSON Schema 核心词汇表

本节中声明的关键字(全部以 "$" 开头)构成了 JSON Schema 核心词汇表。这些关键字要么是处理任何模式或元模式(包括跨多个文档拆分的模式或元模式)所必需的,要么是为了保留用于需要保证互操作性的目的的关键字。

核心词汇表必须始终被视为强制性的,以便引导进一步词汇表的处理。使用 "$vocabulary" 关键字声明正在使用的词汇表的元模式必须显式列出核心词汇表,该词汇表必须具有一个值为 true 的值,表示它是必需的。

此词汇表(且仅此词汇表)的 false 值的行为未定义,当 "$vocabulary" 存在但未包含核心词汇表时,其行为也未定义。但是,建议实现检测这些情况,并在发生这些情况时引发错误。声明元模式可选地使用核心没有意义。

不使用 "$vocabulary" 的元模式必须被视为需要核心词汇表,就像它的 URI 存在且值为 true 一样。

核心词汇表的当前 URI 为:<https://json-schema.fullstack.org.cn/draft/2020-12/vocab/core>。

对应元模式的当前 URI 为:<https://json-schema.fullstack.org.cn/draft/2020-12/meta/core>

虽然 "$" 前缀没有正式保留给核心词汇表,但建议扩展关键字(在词汇表中或其他地方)以除 "$" 之外的字符开头,以避免可能的未来冲突。

8.1. 元模式和词汇表

元模式和词汇表这两个概念用于告知实现如何解释模式。每个模式都有一个元模式,可以使用 "$schema" 关键字声明。

元模式有两个目的

声明正在使用的词汇表
当 "$vocabulary" 关键字出现在元模式中时,它声明了哪些词汇表可用于引用该元模式的模式。词汇表定义关键字语义及其通用语法。
描述有效的模式语法
模式必须成功地针对其元模式进行验证,该元模式约束可用关键字的语法。所描述的语法应与声明的词汇表兼容;虽然可以描述不兼容的语法,但这样的元模式可能不会有用。

元模式与词汇表分离,以允许词汇表以不同的方式组合,并允许元模式作者施加额外的约束,例如禁止某些关键字或执行异常严格的语法验证,这可能会在开发和测试周期中完成。每个词汇表通常识别一个仅包含该词汇表关键字的元模式。

元模式创作是 JSON Schema 的高级用法,因此元模式功能的设计强调灵活性而不是简单性。

8.1.1. "$schema" 关键字

"$schema" 关键字既用作 JSON Schema 方言标识符,也用作资源标识符,该资源本身是一个 JSON Schema,它描述了为此特定方言编写的有效模式集。

此关键字的值**必须**为一个包含方案的URI,并且此 URI **必须**被规范化。当前架构**必须**针对此 URI 标识的元架构有效。

如果此 URI 标识可检索资源,则该资源**应该**为媒体类型“application/schema+json”。

“$schema”关键字**应该**在文档根架构对象中使用,并且**可以**在嵌入式架构资源的根架构对象中使用。它**不应**出现在非资源根架构对象中。如果从文档根架构中缺失,则结果行为是实现定义的。

此属性的值在本和其他文档中以及由其他方定义。

8.1.2. “$vocabulary”关键字

“$vocabulary”关键字用于元架构中,以标识可用于由该元架构描述的架构的词汇表。它还用于指示每个词汇表是必需的还是可选的,这意味着实现**必须**了解必需的词汇表才能成功处理架构。这些信息共同构成一种方言。实现理解的任何词汇表**必须**以与词汇表中包含的语义定义一致的方式进行处理。

此关键字的值**必须**为一个对象。对象中的属性名称**必须**为包含方案的 URI,并且此 URI **必须**被规范化。作为属性名称出现的每个 URI 标识一组特定的关键字及其语义。

URI **可以**是 URL,但可检索资源的性质目前未定义,并保留供将来使用。词汇表作者**可以**使用词汇表规范的 URL(以人类可读的媒体类型,例如 text/html 或 text/plain)作为词汇表 URI。 [CREF1]词汇表文档可能会在即将发布的草案中添加。目前,确定关键字集被认为足够,因为这与元架构验证一起是当前“词汇表”工作方式。任何未来的词汇表文档格式都将被指定为 JSON 文档,因此在此时使用 text/html 或其他非 JSON 格式不会产生任何未来的歧义。

对象属性的值**必须**为布尔值。如果值为真,则不识别词汇表的实现**必须**拒绝处理声明此元架构具有“$schema”的任何架构。如果值为假,则不识别词汇表的实现**应该**继续处理此类架构。如果实现理解词汇表,则该值不会产生任何影响。

根据6.5,未识别的关键字**应该**被视为注释。对于由未识别词汇表定义的关键字,情况仍然如此。目前无法区分由词汇表中定义的未识别关键字与任何词汇表都不属于的未识别关键字。

“$vocabulary”关键字**应该**在任何意图用作元架构的架构文档的根架构中使用。它**不应**出现在子架构中。

在不作为元架构进行处理的架构文档中,**必须**忽略“$vocabulary”关键字。这允许将元架构 M 验证为其自身的元架构 M',而无需验证器理解 M 声明的词汇表。

8.1.2.1. 默认词汇表

如果“$vocabulary”缺失,实现**可以**根据元架构确定行为,前提是它从引用架构的“$schema”关键字的 URI 值识别该元架构。这就是在词汇表存在之前识别行为(例如超架构使用方式)的方式。

如果架构引用的元架构未被识别或缺失,则行为是实现定义的。如果实现继续处理架构,则**必须**假设使用核心词汇表。如果实现是为特定目的构建的,则**应该**假设使用所有与该目的最相关的词汇表。

例如,一个作为验证器的实现**应该**假设使用本规范和配套验证规范中的所有词汇表。

8.1.2.2. 词汇表的不可继承性

请注意,“$vocabulary”上的处理限制意味着使用“$ref”或类似关键字引用其他元架构的元架构不会自动继承这些其他元架构的词汇表声明。所有此类声明必须在每个意图用作元架构的架构文档的根目录中重复。这在示例元架构中有所示。 [CREF2]此要求允许实现找到每个元架构的单个位置中的所有词汇表要求信息。由于架构可扩展性意味着通过引用组合更细粒度的元架构的方式无穷无尽,要求实现预期所有可能性并在引用的元架构中搜索词汇表将过于繁重。

8.1.3. 元架构和词汇表 URI 的更新

在规范草案之间**可以**发布更新的词汇表和元架构 URI 以纠正错误。实现**应该**考虑在本文档草案之后和下一个草案之前日期的 URI,以指示与此处列出的相同的语法和语义。

8.2. 基准 URI、锚点和反引用

为了区分庞大生态系统中的架构,架构通过URI标识,并且可以通过指定其 URI 来嵌入对其他架构的引用。

一些关键字可以接受一个相对的URI 引用,或者一个用于构建相对 URI 引用的值。对于这些关键字,需要建立一个基准 URI 才能解析引用。

8.2.1. “$id”关键字

“$id”关键字使用其规范 URI 标识架构资源。

请注意,此 URI 是一个标识符,不一定是网络定位器。对于可网络寻址的 URL,架构不必从其规范 URI 下载。

如果存在,此关键字的值**必须**为一个字符串,并且**必须**表示一个有效的URI 引用。此 URI 引用**应该**被规范化,并且**必须**解析为一个绝对 URI(不带片段)。因此,“$id”**不应**包含非空片段,并且**不应**包含空片段。

由于在 application/schema+json 媒体类型上下文中,空片段与没有片段的基准 URI 指向同一个资源,因此实现**可以**通过删除片段来规范以空片段结尾的 URI。但是,架构作者**不应**依赖于跨实现的此行为。 [CREF3]主要允许这样做是因为较旧的元架构在其 $id(或之前,id)中有一个空片段。未来的草案可能会完全禁止在“$id”中使用空片段。

此 URI 还用作架构资源中关键字中相对 URI 引用的基准 URI,符合RFC 3986 第 5.1.1 节关于嵌入在内容中的基准 URI 的规定。

子架构中存在“$id”表明子架构构成单个架构文档中的一个独立架构资源。此外,根据RFC 3986 第 5.1.2 节关于封装实体的规定,如果子架构中的“$id”是一个相对 URI 引用,则解析该引用的基准 URI 是父架构资源的 URI。

如果没有任何父架构对象显式地将自身标识为具有“$id”的资源,则基准 URI 是整个文档的 URI,如上一节中给出的步骤所确定。

8.2.1.1. 识别根架构

JSON 架构文档的根架构**应该**包含一个“$id”关键字,该关键字包含一个绝对 URI(包含方案,但不包含片段)。

8.2.2. 定义与位置无关的标识符

使用 JSON 指针片段需要了解架构的结构。当编写旨在提供可重用架构的架构文档时,最好使用不与任何特定结构位置绑定的简单名称片段。这样,子架构就可以重新定位,而无需更新 JSON 指针引用。

“$anchor”和“$dynamicAnchor”关键字用于指定此类片段。它们是标识符关键字,只能用于创建简单名称片段,而不是像“$id”那样用于创建绝对 URI。

将结果片段附加到的基准 URI 是包含相应“$anchor”或“$dynamicAnchor”的架构资源的规范 URI。如上一节所述,这要么是同一架构对象或父架构对象中最接近的“$id”,要么是根据 RFC 3986 确定的文档的基准 URI。

除了 URI 的通常用法之外,“$dynamicAnchor”还表示片段是与“$dynamicRef”关键字一起使用时的扩展点。此低级高级功能使扩展递归架构(如元架构)变得更加容易,而不会对该扩展施加任何特定的语义。有关详细信息,请参见有关“$dynamicRef”的部分。

在大多数情况下,正常的片段行为既足够直观。因此,**建议**使用“$anchor”来创建简单名称片段,除非对“$dynamicAnchor”有明确的需求。

如果存在,此关键字的值**必须**为一个字符串,并且**必须**以字母 ([A-Za-z]) 或下划线 ("_") 开头,后跟任意数量的字母、数字 ([0-9])、连字符 ("-")、下划线 ("_") 和句点 (".")。这与 XML 的NCName 产生式的 US-ASCII 部分匹配。 [CREF4]请注意,锚点字符串不包括 "#" 字符,因为它不是 URI 引用。“$anchor”: “foo” 在 URI 中使用时将变为片段 "#foo”。有关完整示例,请参见下文。

在同一资源中使用“$anchor”和/或“$dynamicAnchor”的任何组合,多次指定同一个片段名称的效果是未定义的。如果检测到此类用法,实现**可以**引发错误。

8.2.3. 架构引用

一些关键字可用于引用要应用于当前实例位置的架构。“$ref”和“$dynamicRef”是应用器关键字,将引用的架构应用于实例。

由于“$ref”和“$dynamicRef”的值是 URI 引用,因此这允许将架构跨多个文件外部化或划分,并提供通过自引用验证递归结构的能力。

由这些关键字生成的已解析 URI 不一定是网络定位器,而只是一个标识符。如果这是一个可网络寻址的 URL,则架构不必从地址下载,并且实现**不应**假设在遇到可网络寻址的 URI 时应该执行网络操作。

8.2.3.1. 使用“$ref”的直接引用

“$ref”关键字是一个应用器,用于引用静态标识的架构。其结果是引用架构的结果。 [CREF5]请注意,此关于结果确定方式的定义意味着其他关键字可以与“$ref”一起出现在同一个架构对象中。

“$ref”关键字的值**必须**为一个字符串,该字符串是一个 URI 引用。相对于当前 URI 基准解析后,它会生成要应用的架构的 URI。此解析在架构加载时执行是安全的,因为评估实例的过程不会改变引用的解析方式。

8.2.3.2. 使用“$dynamicRef”的动态引用

“$dynamicRef” 关键字是一个应用器,允许在运行时推迟完全解析,解析将在每次遇到时(评估实例时)进行。

与 “$dynamicAnchor” 一起,“$dynamicRef” 实现了一种协作扩展机制,它主要在递归模式(自身引用模式)中使用。扩展点和运行时确定的扩展目标都由 “$dynamicAnchor” 定义,只有在使用 “$dynamicRef” 引用时才表现出运行时动态行为。

“$dynamicRef” 属性的值必须是一个字符串,该字符串是一个 URI 引用。解析当前 URI 基准后,它将生成用作运行时解析起点的 URI。此初始解析在模式加载时可以安全执行。

如果最初解析的起始点 URI 包含由 “$dynamicAnchor” 关键字创建的片段,则必须将初始 URI 替换为定义了具有 “$dynamicAnchor” 的相同名称片段的 动态范围 中最外部模式资源的 URI(包括片段)。

否则,其行为与 “$ref” 相同,不需要运行时解析。

有关使用这些关键字的完整示例,请参见附录 C[CREF6]2019 年之前草案中的超模式元模式与本草案之间的差异极大地证明了这些关键字的效用。

8.2.4. 使用 “$defs” 重用模式

“$defs” 关键字为模式作者保留了一个位置,以便将可重用的 JSON 模式内联到更通用的模式中。该关键字不会直接影响验证结果。

该关键字的值必须是一个对象。此对象的每个成员值必须是一个有效的 JSON 模式。

例如,以下模式描述了一个正整数数组,其中正整数约束是 “$defs” 中的子模式

{
    "type": "array",
    "items": { "$ref": "#/$defs/positiveInteger" },
    "$defs": {
        "positiveInteger": {
            "type": "integer",
            "exclusiveMinimum": 0
        }
    }
}

                        

8.3. 使用 “$comment” 进行注释

该关键字为模式作者保留了一个位置,供模式的读者或维护者进行注释。

该关键字的值必须是一个字符串。实现必须不要将此字符串呈现给最终用户。用于编辑模式的工具应该支持显示和编辑此关键字。此关键字的值可以在调试或错误输出中使用,这些输出旨在供使用模式的开发人员使用。

模式词汇表应该允许在包含词汇表关键字的任何对象中使用 “$comment”。除非词汇表明确禁止,否则实现可以假定允许使用 “$comment”。词汇表必须不要指定 “$comment” 的任何效果,超出本规范中描述的效果。

将其他媒体类型或编程语言转换为 application/schema+json 或从 application/schema+json 转换的工具可以选择将该媒体类型或编程语言的本机注释转换为 “$comment” 值或从 “$comment” 值转换。当存在本机注释和 “$comment” 属性时,此类转换的行为取决于实现。

实现可以在处理过程中的任何时间点删除 “$comment” 值。特别是,当部署模式的大小是一个问题时,这允许缩短模式。

实现必须不要根据 “$comment” 属性的存在、不存在或内容采取任何其他操作。特别是,“$comment” 的值必须不要作为注释结果收集。

9. 加载和处理模式

9.1. 加载模式

9.1.1. 初始基准 URI

RFC 3986 第 5.1 节 定义了如何确定文档的默认基准 URI。

提示,模式的初始基准 URI 是找到它的 URI,无论它是一个网络位置、一个本地文件系统还是任何其他可以通过任何已知方案的 URI 识别的状况。

如果模式文档未在 “$id” 中定义明确的基准 URI(嵌入在内容中),则基准 URI 是根据 RFC 3986 第 5 节 确定的。

如果未知源,或未知源的 URI 方案,则可以使用 RFC 3986 第 5.1.4 节 中描述的适合实现特定的默认 URI。建议实现记录它们假设的任何默认基准 URI。

如果模式对象嵌入在其他媒体类型的文档中,则初始基准 URI 是根据该媒体类型的规则确定的。

除非下一节中描述的 “$id” 关键字存在于根模式中,否则此基准 URI 应该被认为是模式文档的根模式资源的规范 URI。

9.1.2. 加载引用的模式

使用 URI 标识远程模式并不一定意味着下载任何内容,而是 JSON 模式实现应该提前了解将要使用的模式以及标识它们的 URI。

当模式被下载时,例如由一个通用的用户代理(它直到运行时才知道要下载哪些模式),请参阅 超媒体用法

实现应该能够将任意 URI 与任意模式关联起来,或者根据验证器对模式的信任程度自动关联模式的 “$id” 给定的 URI。此类 URI 和模式可以在处理实例之前提供给实现,或者可以在处理模式文档时在模式文档中进行注释,生成附录 A 中所示的关联。

一个模式可能(而且很可能)具有多个 URI,但没有方法让一个 URI 标识多个模式。当多个模式尝试标识为同一个 URI 时,验证器应该引发错误条件。

9.1.3. 检测元模式

如果正在检查模式是因为它被另一个模式的 “$schema” 关键字识别为元模式,则实现必须识别它为元模式。这意味着单个模式文档有时可能被视为普通模式,而其他时候则被视为元模式。

在检查本身是元模式的模式的情况下,当实现开始将其作为普通模式处理时,它将根据这些规则进行处理。但是,当第二次加载它作为检查它自己的 “$schema” 值的结果时,它将被视为元模式。因此,同一文档在一个会话的过程中以两种方式处理。

实现可能允许明确地将模式传递为元模式,以实现特定目的,例如预加载常用的元模式并预先检查其词汇表支持要求。元模式作者必须不要期望此类功能在不同实现之间具有互操作性。

9.2. 反引用

模式可以通过已赋予它们的任何 URI 标识,包括 JSON 指针或由 “$id” 直接给定的 URI。在所有情况下,反引用 “$ref” 引用都涉及首先根据 RFC 3986 将其值解析为相对于当前基准 URI 的 URI 引用。

如果结果 URI 标识了当前文档中的模式,或者标识了已提供给实现的另一个模式文档中的模式,则应该自动使用该模式。

例如,考虑以下模式

{
    "$id": "https://example.net/root.json",
    "items": {
        "type": "array",
        "items": { "$ref": "#item" }
    },
    "$defs": {
        "single": {
            "$anchor": "item",
            "type": "object",
            "additionalProperties": { "$ref": "other.json" }
        }
    }
}

                    

当实现遇到 <#/$defs/single> 模式时,它将 “$anchor” 值解析为相对于当前基准 URI 的片段名称,形成 <https://example.net/root.json#item>。

当实现然后查看 <#/items> 模式时,它遇到 <#item> 引用,并将其解析为 <https://example.net/root.json#item>,它已在此同一文档中定义,因此可以自动使用。

当实现遇到对 “other.json” 的引用时,它将其解析为 <https://example.net/other.json>,它没有在此文档中定义。如果具有该标识符的模式已以其他方式提供给实现,则它也可以自动使用。 [CREF7]当引用模式未知时,实现应该怎么做?在哪些情况下允许自动网络反引用?同源策略?用户可配置选项?在由超模式描述的不断发展的 API 的情况下,预计新模式将动态添加到系统中,因此,将绝对要求预加载模式文档是不可行的。

9.2.1. JSON 指针片段和嵌入式模式资源

由于 JSON 指针 URI 片段是根据模式文档的结构构造的,因此嵌入式模式资源及其子模式可以通过相对于其自身规范 URI 或相对于包含资源的 URI 的 JSON 指针片段标识。

从概念上讲,一组链接的模式资源应该表现得完全相同,无论每个资源是使用 模式引用 连接的单独文档,还是结构化为单个文档,其中一个或多个模式资源作为子模式嵌入。

由于涉及 JSON 指针片段的相对于父模式资源 URI 的 URI 在将嵌入式模式移动到单独的文档并进行引用时不再有效,因此应用程序和模式不应该使用此类 URI 来标识嵌入式模式资源或其中的位置。

考虑以下包含另一个嵌入式模式资源的模式文档

{
    "$id": "https://example.com/foo",
    "items": {
        "$id": "https://example.com/bar",
        "additionalProperties": { }
    }
}

                        

URI “https://example.com/foo#/items/additionalProperties” 指向嵌入式资源中 “additionalProperties” 关键字的模式。但是,该模式的规范 URI 是 “https://example.com/bar#/additionalProperties”。

现在考虑以下两个通过引用链接的模式资源,使用 “$ref” 的 URI 值

{
    "$id": "https://example.com/foo",
    "items": {
        "$ref": "bar"
    }
}

{
    "$id": "https://example.com/bar",
    "additionalProperties": { }
}

                        

在这里我们看到,该 “additionalProperties” 子模式的规范 URI 仍然有效,而以 “#/items/$ref” 开头的片段的非规范 URI 现在解析为空。

还要注意,“https://example.com/foo#/items” 在两种安排中都有效,但解析为不同的值。此 URI 最终的功能类似于资源的检索 URI。虽然有效,但最好检查解析后的值,并使用 “$id”(如果该值是子模式),或者解析引用并使用引用目标的 “$id”。

实现可能选择不支持通过非规范 URI 来寻址模式。因此,建议模式作者只使用规范 URI,因为使用非规范 URI 可能会降低模式的互操作性。 [CREF8]这是为了避免要求实现跟踪所有可能的基准 URI 和 JSON 指针片段的整个堆栈,因为如果模式资源被重新组织,除了一个之外的所有堆栈都将很脆弱。有些人认为这很容易,因此没有必要禁止它,而另一些人认为它使模式标识变得复杂,应该禁止。鼓励对此主题提供反馈。

附录 A 提供了此类非规范 URI 的更多示例,以及相应的规范 URI。

9.3. 复合文档

复合模式文档被定义为一个 JSON 文档(有时称为“捆绑”模式),它将多个嵌入式 JSON 模式资源捆绑到同一个文档中,以简化传输。

每个嵌入式 Schema 资源 MUST 被视为一个独立的 Schema 资源,遵循标准的 Schema 加载和处理要求,包括确定词汇支持。

9.3.1. 打包

创建复合 Schema 文档的打包过程被定义为,将指向外部 Schema 资源的引用(例如“$ref”)嵌入到引用文档中。打包 SHOULD 以这样一种方式进行,即基文档和任何被引用/嵌入的文档中的所有 URI(用于引用)都不需要更改。

每个嵌入的 JSON Schema 资源 MUST 使用“$id”关键字识别自身,并且 SHOULD 使用“$schema”关键字识别它使用的方言,位于 Schema 资源的根部。 RECOMMENDED “$id”的 URI 标识符值为绝对 URI。

当引用应用器引用的 Schema 资源被打包时,RECOMMENDED 将 Schema 资源放置在包含 Schema 根部的“$defs”对象的 value 中。现在嵌入式 Schema 资源的“$defs”的 key MAY 为打包的 Schema 的“$id”,或某种应用程序定义的唯一标识符(例如 UUID)。此 key 不打算在 JSON Schema 中引用,但应用程序可以使用它来帮助打包过程。

Schema 资源 MAY 被嵌入到“$defs”以外的位置,其中该位置被定义为 Schema value。

Bundled Schema 资源 MUST NOT 通过替换引用它的 Schema 对象,或通过将 Schema 资源包装在其他应用器关键字中来打包。

为了产生相同的输出,包含 Schema 文档中对以前外部 Schema 资源的引用 MUST NOT 被更改,并且现在解析为使用嵌入式 Schema 资源的“$id”的 Schema。这种相同的输出包括验证评估以及生成的注释或错误中使用的 URI 或路径。

虽然打包过程通常是创建复合 Schema 文档的主要方法,但也可能并且期望某些文档是手工创建的,可能之前没有单个 Schema 资源独立存在。

9.3.2. 不同的和默认的方言

当单个文档中存在多个 Schema 资源时,未定义使用哪个方言处理的 Schema 资源 MUST 使用与封闭资源相同的方言处理。

由于任何可以引用的 Schema 也可以被嵌入,因此嵌入式 Schema 资源 MAY 使用其封闭资源的“$schema”值指定不同的处理方言。

9.3.3. 验证

鉴于复合 Schema 文档可能包含识别为使用不同方言的嵌入式资源,这些文档 SHOULD NOT 通过将元 Schema 应用于复合 Schema 文档作为实例来进行验证。 RECOMMENDED 提供备用验证过程以验证 Schema 文档。每个 Schema 资源 SHOULD 分别针对其关联的元 Schema 进行验证。[CREF9]如果您知道正在验证的内容是 Schema,则可以通过使用“$id”来识别 Schema 是否是复合 Schema 文档,该“$id”标识在文档根部以外使用时的嵌入式资源。

所有嵌入式资源识别为使用相同方言,或其中“$schema”被省略因此默认为封闭资源的方言的复合 Schema 文档,MAY 通过应用相应的元 Schema 来进行验证。

9.4. 注意事项

9.4.1. 防止无限递归

Schema MUST NOT 对实例运行无限循环。例如,如果两个 Schema “#alice”和“#bob”都具有一个指向另一个的“allOf”属性,则一个简单的验证器可能会陷入无限递归循环,试图验证实例。Schema SHOULD NOT 使用这种无限递归嵌套;行为未定义。

9.4.2. 对可能非 Schema 的引用

子 Schema 对象(或布尔值)通过它们与已知应用器关键字或具有保留位置关键字(例如 "$defs",它接受一个或多个子 Schema 作为值)的使用来识别。这些关键字可能是“$defs”和本文档中的标准应用器,或者来自已知词汇的扩展关键字,或者特定于实现的自定义关键字。

未知关键字的多级结构能够引入嵌套的子 Schema,这些子 Schema 将受“$id”的处理规则约束。因此,在这样的未识别结构中拥有引用目标无法可靠地实现,并且由此产生的行为是未定义的。类似地,在已知关键字下,其值为已知不是 Schema 的引用目标会导致未定义的行为,以避免给实现带来检测此类目标的负担。[CREF10]这些场景类似于通过 HTTP 获取 Schema 但收到响应时 Content-Type 不是 application/schema+json。实现当然可以尝试将其解释为 Schema,但源服务器没有保证它实际上是任何这样的东西。因此,将其解释为这种类型的行为具有安全隐患,并且可能产生不可预测的结果。

请注意,具有与“$defs”相同语法和语义的单级自定义关键字不允许任何中间的“$id”关键字,因此在尝试使用任何引用目标作为 Schema 的实现下会正常运行。但是,此行为特定于实现,MUST NOT 依赖于互操作性。

9.5. 关联实例和 Schema

9.5.1. 超媒体的使用

JSON 已被 HTTP 服务器广泛用于自动化 API 和机器人。本节描述了在与支持媒体类型和 Web 链接 的协议一起使用时,如何以更 RESTful 的方式增强 JSON 文档的处理。

9.5.1.1. 链接到 Schema

RECOMMENDED 由 Schema 描述的实例提供指向可下载 JSON Schema 的链接,使用链接关系“describedby”,如 链接数据协议 1.0,第 8.1 节 所定义。

在 HTTP 中,此类链接可以使用 Link 标头 附加到任何响应。此类标头的示例将是

        
        Link: <https://example.com/my-hyper-schema>; rel="describedby"
        
                            

9.5.1.2. 通过 HTTP 的使用

当用于通过网络的超媒体系统时,HTTP 通常是用于分发 Schema 的首选协议。如果行为不端的客户端比必要时更频繁地从网络上提取 Schema,而实际上可以将 Schema 缓存很长时间,那么它们可能会给服务器维护人员带来问题。

HTTP 服务器 SHOULD 对 JSON Schema 设置长期缓存标头。HTTP 客户端 SHOULD 遵守缓存标头,并且不要在它们的有效期内重新请求文档。分布式系统 SHOULD 使用共享缓存和/或缓存代理。

客户端 SHOULD 设置或添加特定于 JSON Schema 实现或软件产品的 User-Agent 标头。由于符号按重要性降序排列,因此 JSON Schema 库名称/版本应位于更通用的 HTTP 库名称(如果有)之前。例如

        
        User-Agent: product-name/5.4.1 so-cool-json-schema/1.0.2 curl/7.43.0
        
                            

客户端 SHOULD 能够使用“From”标头发出请求,以便服务器运营商可以联系可能行为不端的脚本的所有者。

10. 用于应用子 Schema 的词汇

本节定义了 RECOMMENDED 作为其他词汇基础使用的应用器关键字词汇。

不使用“$vocabulary”的元 Schema SHOULD 被视为需要此词汇,就好像它的 URI 存在并且值为 true 一样。

此词汇的当前 URI,称为应用器词汇,为:<https://json-schema.fullstack.org.cn/draft/2020-12/vocab/applicator>。

相应元 Schema 的当前 URI 为:<https://json-schema.fullstack.org.cn/draft/2020-12/meta/applicator>.

在规范草案之间**可以**发布更新的词汇表和元架构 URI 以纠正错误。实现**应该**考虑在本文档草案之后和下一个草案之前日期的 URI,以指示与此处列出的相同的语法和语义。

10.1. 关键字独立性

Schema 关键字通常独立运行,不会影响彼此的结果。

为了方便 Schema 作者,此词汇中的关键字之间存在一些例外

10.2. 用于在适当位置应用子 Schema 的关键字

这些关键字将子 Schema 应用于实例中与父 Schema 被应用的位置相同的位置。它们允许以各种方式组合或修改子 Schema 结果。

这些关键字的子 Schema 完全独立地评估实例,这样一来,一个子 Schema 的结果 MUST NOT 影响兄弟子 Schema 的结果。因此,子 Schema 可以以任何顺序应用。

10.2.1. 用于用逻辑应用子 Schema 的关键字

这些关键字对应于用于组合或修改子 Schema 的布尔断言结果的逻辑运算符。它们不会对注释收集产生直接影响,尽管它们使相同的注释关键字能够使用不同的值应用于实例位置。注释关键字定义了它们自己的规则来组合这些值。

10.2.1.1. allOf

此关键字的值 MUST 为非空数组。数组的每个项目 MUST 为有效的 JSON Schema。

如果实例对由此关键字的值定义的所有 Schema 成功验证,则它将对该关键字成功验证。

10.2.1.2. anyOf

此关键字的值 MUST 为非空数组。数组的每个项目 MUST 为有效的 JSON Schema。

如果实例对由此关键字的值定义的至少一个 Schema 成功验证,则它将对该关键字成功验证。请注意,当收集注释时,MUST 检查所有子 Schema,以便从成功验证的每个子 Schema 收集注释。

10.2.1.3. oneOf

此关键字的值 MUST 为非空数组。数组的每个项目 MUST 为有效的 JSON Schema。

如果实例对由此关键字的值定义的恰好一个 Schema 成功验证,则它将对该关键字成功验证。

10.2.1.4. not

此关键字的值 MUST 为有效的 JSON Schema。

如果实例未能对由该关键字定义的 Schema 成功验证,则它对该关键字有效。

10.2.2. 用于有条件地应用子 Schema 的关键字

其中三个关键字协同工作,根据另一个子 Schema 的结果实现子 Schema 的有条件应用。第四个是特定条件情况的快捷方式。

"if"、"then" 和 "else" MUST NOT 在子 Schema 边界之间相互交互。换句话说,一个 "allOf" 分支中的 "if" MUST NOT 影响另一个分支中的 "then" 或 "else"。

当 "if"、"then" 或 "else" 不存在时,没有默认行为。特别地,MUST NOT 将它们视为存在并具有空 Schema,并且当 "if" 不存在时,"then" 和 "else" MUST 被完全忽略。

10.2.2.1. if

此关键字的值 MUST 为有效的 JSON Schema。

此关键字的子 Schema 的验证结果对整体验证结果没有直接影响。相反,它控制评估 "then" 或 "else" 关键字中的哪一个。

对该关键字的子 Schema 成功验证的实例 MUST 也对 "then" 关键字的子 Schema 值有效(如果存在)。

无法根据此关键字的子模式进行验证的实例,如果存在,也必须根据“else”关键字的子模式值进行验证。

如果正在收集注释,则会按照通常方式从此关键字的子模式中收集注释,包括当关键字存在但没有“then”或“else”时。

10.2.2.2. then

此关键字的值 MUST 为有效的 JSON Schema。

当存在“if”时,如果实例成功根据其子模式进行验证,则如果实例也成功根据此关键字的子模式进行验证,则验证相对于此关键字成功。

当“if”不存在,或者实例无法根据其子模式进行验证时,此关键字没有任何作用。在这种情况下,实现必须不针对此关键字评估实例,无论是出于验证还是注释收集目的。

10.2.2.3. else

此关键字的值 MUST 为有效的 JSON Schema。

当存在“if”时,如果实例无法根据其子模式进行验证,则如果实例成功根据此关键字的子模式进行验证,则验证相对于此关键字成功。

当“if”不存在,或者实例成功根据其子模式进行验证时,此关键字没有任何作用。在这种情况下,实现必须不针对此关键字评估实例,无论是出于验证还是注释收集目的。

10.2.2.4. dependentSchemas

此关键字指定如果实例是对象并且包含特定属性,则将对其进行评估的子模式。

此关键字的值必须是一个对象。对象中的每个值必须是一个有效的 JSON Schema。

如果对象键是实例中的属性,则整个实例必须根据子模式进行验证。它的使用取决于该属性的存在。

省略此关键字的行为与空对象相同。

10.3. 将子模式应用于子实例的关键字

这些关键字中的每一个都定义了一个规则,用于将它的子模式应用于子实例,特别是对象属性和数组项,以及组合它们的結果。

10.3.1. 将子模式应用于数组的关键字

10.3.1.1. prefixItems

“prefixItems”的值必须是一个非空数组,其中包含有效的 JSON Schema。

如果实例的每个元素都根据相同位置的模式(如果有)进行验证,则验证成功。此关键字不限制数组的长度。如果数组长度大于此关键字的值,则此关键字仅验证匹配长度的前缀。

此关键字生成一个注释值,该值是此关键字应用子模式的最大索引。如果子模式已应用于实例的每个索引,则该值可以是布尔值 true,例如由“items”关键字生成的。此注释会影响“items”和“unevaluatedItems”的行为。

省略此关键字的行为与空数组相同。

10.3.1.2. items

“items”的值必须是一个有效的 JSON Schema。

此关键字将它的子模式应用于所有实例元素,这些元素的索引大于同一模式对象中“prefixItems”数组的长度,如该“prefixItems”关键字的注释结果所报告。如果不存在这样的注释结果,则“items”将它的子模式应用于所有实例数组元素。[CREF11]请注意,没有“prefixItems”的“items”的行为与之前草案中“items”的模式形式相同。当存在“prefixItems”时,“items”的行为与以前的“additionalItems”关键字相同。

如果“items”子模式应用于实例数组中的任何位置,则它会生成一个布尔值 true 的注释结果,表示所有剩余的数组元素已根据此关键字的子模式进行评估。

省略此关键字的行为与空模式相同。

实现可以选择以其他方式实现或优化此关键字,以产生相同的效果,例如通过直接检查“prefixItems”数组的存在和大小。不支持注释收集的实现必须这样做。

10.3.1.3. contains

此关键字的值必须是一个有效的 JSON Schema。

如果数组实例中的至少一个元素根据给定模式进行验证,则该实例相对于“contains”有效。子模式必须应用于每个数组元素,即使在找到第一个匹配项之后也是如此,以便收集用于其他关键字的注释。这是为了确保收集所有可能的注释。

从逻辑上讲,将值子模式应用于数组中的每个项的验证结果必须与“false”进行 OR 运算,从而产生一个整体验证结果。

此关键字生成一个注释值,该值是一个数组,包含此关键字在应用其子模式时成功验证的索引,按升序排列。如果子模式在应用于实例的每个索引时成功验证,则该值可以是布尔值“true”。如果此关键字的模式应用于的实例数组为空,则注释必须存在。

10.3.2. 将子模式应用于对象的关键字

10.3.2.1. properties

“properties”的值必须是一个对象。此对象的每个值必须是一个有效的 JSON Schema。

如果实例中出现的每个名称都在此关键字的值中作为一个名称出现,则对于该名称的子实例成功根据相应的模式进行验证,则验证成功。

此关键字的注释结果是由此关键字匹配的实例属性名称集。

省略此关键字的行为与空对象相同。

10.3.2.2. patternProperties

“patternProperties”的值必须是一个对象。此对象的每个属性名称都应根据 ECMA-262 正则表达式方言是一个有效的正则表达式。此对象的每个属性值都必须是一个有效的 JSON Schema。

如果实例中出现的每个名称都与作为此关键字的值中属性名称出现的任何正则表达式匹配,则对于该名称的子实例成功根据与匹配的正则表达式对应的每个模式进行验证,则验证成功。

此关键字的注释结果是由此关键字匹配的实例属性名称集。

省略此关键字的行为与空对象相同。

10.3.2.3. additionalProperties

“additionalProperties”的值必须是一个有效的 JSON Schema。

此关键字的行为取决于同一模式对象中“properties”和“patternProperties”的存在和注释结果。使用“additionalProperties”进行验证仅适用于实例名称的子值,这些名称未出现在“properties”或“patternProperties”的注释结果中。

对于所有此类属性,如果子实例根据“additionalProperties”模式进行验证,则验证成功。

此关键字的注释结果是由此关键字的子模式验证的实例属性名称集。

省略此关键字的行为与空模式相同。

实现可以选择以其他方式实现或优化此关键字,以产生相同的效果,例如通过直接检查“properties”中的名称和“patternProperties”中的模式与实例属性集进行匹配。不支持注释收集的实现必须这样做。

10.3.2.4. propertyNames

“propertyNames”的值必须是一个有效的 JSON Schema。

如果实例是对象,则此关键字验证实例中每个属性名称是否根据提供的模式进行验证。请注意,模式正在测试的属性名称始终是一个字符串。

省略此关键字的行为与空模式相同。

11. 未评估位置的词汇表

这些关键字的目的是使模式作者能够将子模式应用于尚未成功根据任何相邻关键字的任何动态范围子模式进行评估的数组项或对象属性。

这些实例项或属性可能尚未成功根据一个或多个相邻关键字子模式进行评估,例如当“anyOf”的分支中的断言失败时。此类失败的评估不被视为对该项或属性是否已评估有贡献。只有成功的评估才会被考虑。

如果数组中的项或对象属性“已成功评估”,则从逻辑上讲,它被认为在预期对象或数组的表示形式方面是有效的。例如,如果子模式表示一辆汽车,它需要 2-4 个轮子,而“wheels”的值为 6,则实例对象不被“评估”为一辆汽车,并且“wheels”属性被认为是“未评估(成功作为一个已知的事物)”,并且不保留任何注释。

回想一下,相邻关键字是同一个模式对象中的关键字,并且动态范围子模式包括引用目标和词法子模式。

这些关键字的行为取决于应用于正在验证的实例位置的相邻关键字的注释结果。

不使用“$vocabulary”的元 Schema SHOULD 被视为需要此词汇,就好像它的 URI 存在并且值为 true 一样。

此词汇表的当前 URI,称为未评估应用程序词汇表,是:<https://json-schema.fullstack.org.cn/draft/2020-12/vocab/unevaluated>。

对应元模式的当前 URI 是:<https://json-schema.fullstack.org.cn/draft/2020-12/meta/unevaluated>.

在规范草案之间**可以**发布更新的词汇表和元架构 URI 以纠正错误。实现**应该**考虑在本文档草案之后和下一个草案之前日期的 URI,以指示与此处列出的相同的语法和语义。

11.1. 关键字独立性

模式关键字通常独立运行,不会影响彼此的结果。但是,此词汇表中的关键字是显着的例外

11.2. unevaluatedItems

“unevaluatedItems”的值必须是一个有效的 JSON Schema。

此关键字的行为取决于应用于正在验证的实例位置的相邻关键字的注释结果。具体来说,来自“prefixItems”、“items”和“contains”的注释,这些注释可以在这些关键字与“unevaluatedItems”关键字相邻时从这些关键字获得。这三个注释以及“unevaluatedItems”也可能来自任何和所有相邻的就地应用器关键字。这包括但不限于本文档中定义的就地应用器。

如果没有相关的注释,则“unevaluatedItems”子模式必须应用于数组中的所有位置。如果来自任何相关注释的布尔值 true 值存在,则必须忽略“unevaluatedItems”。否则,子模式必须应用于任何大于“prefixItems”的最大注释值的索引,该索引未出现在“contains”的任何注释值中。

这意味着“prefixItems”、“items”、“contains”和所有就地应用器必须在此关键字可以评估之前进行评估。扩展关键字的作者不得定义需要在此关键字之后评估的就地应用器。

如果“unevaluatedItems”子模式应用于实例数组中的任何位置,则它会生成一个布尔值 true 的注释结果,类似于“items”的行为。

省略此关键字的行为与空模式相同。

11.3. unevaluatedProperties

“unevaluatedProperties”的值必须是一个有效的 JSON Schema。

此关键字的行为取决于应用于正在验证的实例位置的相邻关键字的注释结果。具体来说,来自“properties”、“patternProperties”和“additionalProperties”的注释,这些注释可以在这些关键字与“unevaluatedProperties”关键字相邻时从这些关键字获得。这三个注释以及“unevaluatedProperties”也可能来自任何和所有相邻的就地应用器关键字。这包括但不限于本文档中定义的就地应用器。

使用“unevaluatedProperties”进行验证仅适用于实例名称的子值,这些名称未出现在应用于正在验证的实例位置的“properties”、“patternProperties”、“additionalProperties”或“unevaluatedProperties”注释结果中。

对于所有此类属性,如果子实例根据“unevaluatedProperties”模式进行验证,则验证成功。

这意味着 "properties"、"patternProperties"、"additionalProperties" 以及所有就地应用器 **必须** 在此关键字被评估之前进行评估。扩展关键字的作者 **不得** 定义需要在此关键字之后评估的就地应用器。

此关键字的注释结果是由此关键字的子模式验证的实例属性名称集。

省略此关键字的行为与空模式相同。

12. 输出格式

JSON Schema 被定义为平台无关的。因此,为了提高跨平台的兼容性,实现 **应该** 符合标准的验证输出格式。本节描述了消费者正确解释验证结果所需的最低要求。

12.1. 格式

JSON Schema 输出使用 JSON Schema 数据实例模型定义,如第 4.2.1 节所述。实现 **可以** 偏离此定义,只要符合其特定语言和平台支持的方式,但 **建议** 将输出通过序列化或其他方式转换为本文档中定义的 JSON 格式。

12.2. 输出格式

本规范定义了四种输出格式。有关每种格式的要求,请参阅 "输出结构" 部分。

实现 **应该** 提供至少一个 "flag"、"basic" 或 "detailed" 格式,并且 **可以** 提供 "verbose" 格式。如果它提供一个或多个 "detailed" 或 "verbose" 格式,那么它 **必须** 也提供 "flag" 格式。实现 **应该** 在其文档中指定其支持的格式。

12.3. 最小信息

除了简单的 "flag" 输出外,其他信息对于帮助调试模式或实例非常有用。每个子结果 **应该** 至少包含本节中包含的信息。

包含所有这些组件的单个对象被认为是一个输出单元。

实现 **可以** 选择提供其他信息。

12.3.1. 关键字相对位置

遵循验证路径的验证关键字的相对位置。该值 **必须** 以 JSON 指针表示,并且 **必须** 包含任何按引用应用器,例如 "$ref" 或 "$dynamicRef"。

#/properties/width/$ref/minimum

                        

请注意,由于包含这些按引用应用器关键字,因此此指针可能无法通过正常的 JSON 指针过程解析。

此信息的 JSON 键为 "keywordLocation"。

12.3.2. 关键字绝对位置

验证关键字的绝对、反引用位置。该值 **必须** 以使用相关模式对象的规范 URI 的完整 URI 表示,并且 **不得** 包含按引用应用器,例如 "$ref" 或 "$dynamicRef" 作为非终端路径组件。如果错误或注释是针对该关键字,例如无法解析的引用,它 **可以** 以这些关键字结尾。 [CREF12]请注意,这里的 "绝对" 是指 "绝对文件系统路径"(意味着完整位置),而不是 RFC 3986 中的 "绝对 URI" 术语(意味着有方案但没有片段)。关键字绝对位置将具有片段以标识关键字。

https://example.com/schemas/common#/$defs/count/minimum

                        

仅当动态范围没有传递引用或模式未声明其 "$id" 为绝对 URI 时,此信息 **可以** 省略。

此信息的 JSON 键为 "absoluteKeywordLocation"。

12.3.3. 实例位置

正在验证的实例中 JSON 值的位置。该值 **必须** 以 JSON 指针表示。

此信息的 JSON 键为 "instanceLocation"。

12.3.4. 错误或注释

验证产生的错误或注释。

对于错误,本规范未定义消息的具体措辞。实现需要提供此信息。

对于注释,每个产生注释的关键字都指定其格式。默认情况下,它就是关键字的值。

验证失败的 JSON 键为 "error";验证成功的 JSON 键为 "annotation"。

12.3.5. 嵌套结果

对于两个分层结构,此属性将保存嵌套错误和注释。

验证失败中嵌套结果的 JSON 键为 "errors";验证成功中嵌套结果的 JSON 键为 "annotations"。请注意复数形式,因为具有嵌套结果的关键字也可以具有本地错误或注释。

12.4. 输出结构

输出 **必须** 是一个包含名为 "valid" 的布尔属性的对象。当需要有关结果的其他信息时,输出 **必须** 也包含 "errors" 或 "annotations",如下所述。

对于以下示例,将使用以下模式和实例。

{
  "$id": "https://example.com/polygon",
  "$schema": "https://json-schema.fullstack.org.cn/draft/2020-12/schema",
  "$defs": {
    "point": {
      "type": "object",
      "properties": {
        "x": { "type": "number" },
        "y": { "type": "number" }
      },
      "additionalProperties": false,
      "required": [ "x", "y" ]
    }
  },
  "type": "array",
  "items": { "$ref": "#/$defs/point" },
  "minItems": 3
}

[
  {
    "x": 2.5,
    "y": 1.3
  },
  {
    "x": 1,
    "z": 6.7
  }
]

                    

此实例将无法通过验证并产生错误,但可以轻松推断出产生注释的通过模式的示例。

具体来说,它将产生的错误是

请注意,这些示例中描述的错误消息措辞不是本规范的要求。实现 **应该** 为其受众编写定制的错误消息,或者提供允许用户编写自己的消息的模板机制。

12.4.1. Flag

在最简单的情况下,只需要满足 "valid" 属性的布尔结果即可。

{
  "valid": false
}

                        

由于此格式不返回任何错误或注释,因此 **建议** 实现使用短路逻辑,只要可以确定结果就立即返回失败或成功。例如,如果 "anyOf" 关键字包含五个子模式,并且第二个子模式通过了,则无需检查另外三个。逻辑可以简单地返回成功。

12.4.2. Basic

"Basic" 结构是输出单元的扁平列表。

{
  "valid": false,
  "errors": [
    {
      "keywordLocation": "",
      "instanceLocation": "",
      "error": "A subschema had errors."
    },
    {
      "keywordLocation": "/items/$ref",
      "absoluteKeywordLocation":
        "https://example.com/polygon#/$defs/point",
      "instanceLocation": "/1",
      "error": "A subschema had errors."
    },
    {
      "keywordLocation": "/items/$ref/required",
      "absoluteKeywordLocation":
        "https://example.com/polygon#/$defs/point/required",
      "instanceLocation": "/1",
      "error": "Required property 'y' not found."
    },
    {
      "keywordLocation": "/items/$ref/additionalProperties",
      "absoluteKeywordLocation":
        "https://example.com/polygon#/$defs/point/additionalProperties",
      "instanceLocation": "/1/z",
      "error": "Additional property 'z' found but was invalid."
    },
    {
      "keywordLocation": "/minItems",
      "instanceLocation": "",
      "error": "Expected at least 3 items but found 2"
    }
  ]
}

                        

12.4.3. Detailed

"Detailed" 结构基于模式,对于人和机器来说更易于阅读。以这种方式组织结构使得错误之间的关联更加明显。例如,缺少 "y" 属性和额外的 "z" 属性都源于实例中的相同位置,这一点在 "Basic" 结构中并不立即显而易见。在层次结构中,这种相关性更容易识别。

以下规则控制结果对象的构建

分支节点不需要错误消息或注释。

{
  "valid": false,
  "keywordLocation": "",
  "instanceLocation": "",
  "errors": [
    {
      "valid": false,
      "keywordLocation": "/items/$ref",
      "absoluteKeywordLocation":
        "https://example.com/polygon#/$defs/point",
      "instanceLocation": "/1",
      "errors": [
        {
          "valid": false,
          "keywordLocation": "/items/$ref/required",
          "absoluteKeywordLocation":
            "https://example.com/polygon#/$defs/point/required",
          "instanceLocation": "/1",
          "error": "Required property 'y' not found."
        },
        {
          "valid": false,
          "keywordLocation": "/items/$ref/additionalProperties",
          "absoluteKeywordLocation":
            "https://example.com/polygon#/$defs/point/additionalProperties",
          "instanceLocation": "/1/z",
          "error": "Additional property 'z' found but was invalid."
        }
      ]
    },
    {
      "valid": false,
      "keywordLocation": "/minItems",
      "instanceLocation": "",
      "error": "Expected at least 3 items but found 2"
    }
  ]
}

                        

12.4.4. Verbose

"Verbose" 结构是一个完全实现的层次结构,与模式完全匹配。此结构在表单生成和验证中具有应用,其中错误的位置很重要。

此结构与 "Detailed" 结构的主要区别在于返回所有结果。这包括将被删除的子模式验证结果(例如,验证失败的注释、`not` 关键字中的成功验证等)。因此,**建议** 每个节点也携带一个 `valid` 属性,以指示该节点的验证结果。

由于此输出结构可能很大,因此出于简洁性,这里提供了一个更小的示例。上面示例的完整输出结构的 URI 为: <https://json-schema.fullstack.org.cn/draft/2020-12/output/verbose-example>.

// schema
{
  "$id": "https://example.com/polygon",
  "$schema": "https://json-schema.fullstack.org.cn/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "validProp": true,
  },
  "additionalProperties": false
}

// instance
{
  "validProp": 5,
  "disallowedProp": "value"
}

// result
{
  "valid": false,
  "keywordLocation": "",
  "instanceLocation": "",
  "errors": [
    {
      "valid": true,
      "keywordLocation": "/type",
      "instanceLocation": ""
    },
    {
      "valid": true,
      "keywordLocation": "/properties",
      "instanceLocation": ""
    },
    {
      "valid": false,
      "keywordLocation": "/additionalProperties",
      "instanceLocation": "",
      "errors": [
        {
          "valid": false,
          "keywordLocation": "/additionalProperties",
          "instanceLocation": "/disallowedProp",
          "error": "Additional property 'disallowedProp' found but was invalid."
        }
      ]
    }
  ]
}

                        

12.4.5. 输出验证模式

为了方便起见,已提供 JSON Schema 来验证实现生成的输出。其 URI 为: <https://json-schema.fullstack.org.cn/draft/2020-12/output/schema>.

13. 安全注意事项

模式和实例都是 JSON 值。因此,RFC 8259 中定义的所有安全注意事项都适用。

实例和模式通常由不受信任的第三方编写,并部署在公共互联网服务器上。验证器应注意,解析和根据模式进行验证不会消耗过多的系统资源。验证器 **不得** 陷入无限循环。

恶意方可能会导致实现重复收集一个非常大的值作为注释。实现 **应该** 在这种情况下防止过度消耗系统资源。

服务器 **必须** 确保恶意方无法通过上传具有预先存在的或非常类似的 "$id" 的模式来更改现有模式的功能。

各个 JSON Schema 词汇表也可能具有其自身的安全注意事项。有关更多信息,请咨询相应的规范。

模式作者应注意 "$comment" 内容,因为恶意实现可能会违反规范将它们显示给最终用户,或者如果预期这种行为则不会删除它们。

恶意模式作者可能会在 "$comment" 中放置可执行代码或其他危险材料。实现 **不得** 解析或基于 "$comment" 内容采取任何操作。

14. IANA 注意事项

14.1. application/schema+json

JSON Schema 的提议 MIME 媒体类型定义如下

14.2. application/schema-instance+json

需要 JSON Schema 特定媒体类型的 JSON Schema 实例的提议 MIME 媒体类型定义如下

15. 参考资料

15.1. 规范性引用

[ecma262] "ECMA-262,第 11 版规范",2020 年 6 月。
[RFC2119] Bradner, S.,"RFC 中用于指示需求级别的关键词",BCP 14,RFC 2119,DOI 10.17487/RFC2119,1997 年 3 月。
[RFC3986] Berners-Lee, T.Fielding, R.L. Masinter,"统一资源标识符 (URI):通用语法",STD 66,RFC 3986,DOI 10.17487/RFC3986,2005 年 1 月。
[RFC6839] Hansen, T.A. Melnikov,"媒体类型结构化语法后缀的补充",RFC 6839,DOI 10.17487/RFC6839,2013年1月。
[RFC6901] Bryan, P.Zyp, K.M. Nottingham,"JavaScript 对象表示法 (JSON) 指针",RFC 6901,DOI 10.17487/RFC6901,2013年4月。
[RFC8259] Bray, T.,"JavaScript 对象表示法 (JSON) 数据交换格式",STD 90,RFC 8259,DOI 10.17487/RFC8259,2017年12月。
[W3C.REC-ldp-20150226] Speicher, S.Arwe, J.A. Malhotra,"链接数据平台 1.0",万维网联盟建议 REC-ldp-20150226,2015年2月。

15.2. 说明性参考

[json-hyper-schema] Andrews, H.A. Wright,"JSON 超级模式:JSON 超媒体注释的词汇表",互联网草案 draft-handrews-json-schema-hyperschema-02,2017年11月。
[json-schema-validation] Wright, A.Andrews, H.B. Hutton,"JSON 模式验证:JSON 结构验证的词汇表",互联网草案 draft-bhutton-json-schema-validation-00,2020年12月。
[RFC6596] Ohye, M.J. Kupke,"规范链接关系",RFC 6596,DOI 10.17487/RFC6596,2012年4月。
[RFC7049] Bormann, C.P. Hoffman,"简洁二进制对象表示 (CBOR)",RFC 7049,DOI 10.17487/RFC7049,2013年10月。
[RFC7231] Fielding, R.J. Reschke,"超文本传输协议 (HTTP/1.1):语义和内容",RFC 7231,DOI 10.17487/RFC7231,2014年6月。
[RFC8288] Nottingham, M.,"网页链接",RFC 8288,DOI 10.17487/RFC8288,2017年10月。
[W3C.WD-fragid-best-practices-20121025] Tennison, J.,"片段标识符和媒体类型定义的最佳实践",万维网联盟工作草案 WD-fragid-best-practices-20121025,2012年10月。
[xml-names] Bray, T.Hollander, D.Layman, A.R. Tobin,"XML 中的命名空间 1.1(第二版)",2006年8月。

附录 A. 模式识别示例

考虑以下模式,它显示了“$id”用于识别根模式和各种子模式,以及“$anchor”用于定义普通名称片段标识符。

{
    "$id": "https://example.com/root.json",
    "$defs": {
        "A": { "$anchor": "foo" },
        "B": {
            "$id": "other.json",
            "$defs": {
                "X": { "$anchor": "bar" },
                "Y": {
                    "$id": "t/inner.json",
                    "$anchor": "bar"
                }
            }
        },
        "C": {
            "$id": "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"
        }
    }
}

                

以下 URI 编码的 JSON 指针(相对于根模式)处的模式具有以下基本 URI,并且可以通过任何列出的 URI 识别,符合第 5 节和第 9.2.1 节。

# (文档根)
规范绝对 URI(也是基本 URI)
https://example.com/root.json
具有指针片段的规范 URI
https://example.com/root.json#

#/$defs/A
基本 URI
https://example.com/root.json
具有普通片段的规范 URI
https://example.com/root.json#foo
具有指针片段的规范 URI
https://example.com/root.json#/$defs/A

#/$defs/B
基本 URI
https://example.com/other.json
具有指针片段的规范 URI
https://example.com/other.json#
相对于 root.json 的非规范 URI,带有片段
https://example.com/root.json#/$defs/B

#/$defs/B/$defs/X
基本 URI
https://example.com/other.json
具有普通片段的规范 URI
https://example.com/other.json#bar
具有指针片段的规范 URI
https://example.com/other.json#/$defs/X
相对于 root.json 的非规范 URI,带有片段
https://example.com/root.json#/$defs/B/$defs/X

#/$defs/B/$defs/Y
基本 URI
https://example.com/t/inner.json
具有普通片段的规范 URI
https://example.com/t/inner.json#bar
具有指针片段的规范 URI
https://example.com/t/inner.json#
相对于 other.json 的非规范 URI,带有片段
https://example.com/other.json#/$defs/Y
相对于 root.json 的非规范 URI,带有片段
https://example.com/root.json#/$defs/B/$defs/Y

#/$defs/C
基本 URI
urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f
具有指针片段的规范 URI
urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f#
相对于 root.json 的非规范 URI,带有片段
https://example.com/root.json#/$defs/C

附录 B. 操作模式文档和引用

已经创建了各种工具来重新排列模式文档,具体取决于引用(“$ref”)的出现方式和位置。本附录讨论了哪些用例和操作符合本规范。

B.1. 将模式资源捆绑到单个文档中

一组旨在一起使用的模式资源可以组织成每个资源都有自己的模式文档,所有资源都在同一个模式文档中,或者介于两者之间的任何粒度的文档分组。

存在许多工具可以执行各种类型的引用移除。一个常见的用例是生成一个单一文件,其中所有引用都可以在该文件中解析。这通常用于简化分发,或简化编码,以便 JSON 模式库的各种调用不必跟踪和加载大量的资源。

只要所有静态引用(例如“$ref”)都使用解析为规范 URI 的 URI 引用,并且所有模式资源的根模式中都包含绝对 URI 作为“$id”,那么这种转换就可以安全且可逆地完成。

满足这些条件后,可以将每个外部资源复制到“$defs”下,而不会破坏资源的模式对象之间的任何引用,也不会更改验证或注释结果的任何方面。假设每个资源在“$defs”下的名称都是唯一的,因为它们不会出现在嵌入资源的规范 URI 中,所以这些名称不会影响行为。

B.2. 引用移除并不总是安全的

尝试移除所有引用并生成一个模式文档,并不一定在所有情况下都能生成与原始形式具有相同行为的模式。

由于“$ref”现在被视为任何其他关键字,并且在相同的模式对象中允许其他关键字,因此在所有情况下完全支持非递归“$ref”移除可能需要相对复杂的模式操作。本规范的范围不包括确定或提供一组安全的“$ref”移除转换,因为它们不仅取决于模式结构,还取决于预期的用法。

附录 C. 递归模式扩展的示例

考虑以下两个模式,它们描述了一个简单的递归树结构,其中树中的每个节点都可以具有任何类型的“data”字段。第一个模式允许并忽略其他实例属性。第二个模式更加严格,只允许“data”和“children”属性。还显示了一个包含拼写错误的“data”(写成“daat”)的实例示例。

// tree schema, extensible
{
    "$schema": "https://json-schema.fullstack.org.cn/draft/2020-12/schema",
    "$id": "https://example.com/tree",
    "$dynamicAnchor": "node",

    "type": "object",
    "properties": {
        "data": true,
        "children": {
            "type": "array",
            "items": {
                "$dynamicRef": "#node"
            }
        }
    }
}

// strict-tree schema, guards against misspelled properties
{
    "$schema": "https://json-schema.fullstack.org.cn/draft/2020-12/schema",
    "$id": "https://example.com/strict-tree",
    "$dynamicAnchor": "node",

    "$ref": "tree",
    "unevaluatedProperties": false
}

// instance with misspelled field
{
    "children": [ { "daat": 1 } ]
}

                

当我们加载这两个模式时,我们会注意到每个模式中都存在名为“node”的“$dynamicAnchor”(注意缺少“#”,因为这仅仅是名称),从而导致以下完整的模式 URI

此外,JSON 模式实现会跟踪这些片段是由“$dynamicAnchor”创建的事实。

如果我们将“strict-tree”模式应用于实例,我们将遵循“$ref”到“tree”模式,检查其“children”子模式,并在其“items”子模式中找到“$dynamicRef”:到“#node”(注意“#”用于 URI 片段语法)。该引用解析为“https://example.com/tree#node”,这是一个带有由“$dynamicAnchor”创建的片段的 URI。因此,我们必须在遵循引用之前检查动态范围。

此时,动态路径为“#/$ref/properties/children/items/$dynamicRef”,动态范围包含(从最外层范围到最内层范围)

  1. "https://example.com/strict-tree#"
  2. "https://example.com/tree#"
  3. "https://example.com/tree#/properties/children"
  4. "https://example.com/tree#/properties/children/items"

由于我们正在寻找一个普通名称片段,它可以在模式资源中的任何地方定义,因此 JSON 指针片段与该检查无关。这意味着我们可以删除这些片段并消除连续的重复项,生成

  1. "https://example.com/strict-tree"
  2. "https://example.com/tree"

在这种情况下,最外层资源也具有由“$dynamicAnchor”定义的“node”片段。因此,我们不是将“$dynamicRef”解析为“https://example.com/tree#node”,而是将其解析为“https://example.com/strict-tree#node”。

这样,“tree”模式中的递归将递归到“strict-tree”的根,而不是只将“strict-tree”应用于实例根,而是将“tree”应用于实例子节点。

此示例显示了每个模式中相同位置的“$dynamicAnchor”,具体是资源根模式。由于普通名称片段独立于 JSON 结构,因此即使节点模式对象中的一个或两个被移动到“$defs”下,这也同样有效。正是匹配的“$dynamicAnchor”值告诉我们如何解析动态引用,而不是 JSON 结构中的任何关联。

附录 D. 使用词汇表

D.1. 词汇表和元模式作者的最佳实践

词汇表作者应该注意避免关键字名称冲突,如果词汇表旨在广泛使用,并且可能与其他词汇表结合使用。JSON 模式没有提供任何正式的命名空间系统,但也没有限制关键字名称,允许使用任意数量的命名空间方法。

词汇表可以相互构建,例如通过定义其关键字相对于另一个词汇表中关键字的行为,或者通过使用另一个词汇表中的关键字,但具有受限或扩展的接受值集。并非所有此类词汇表重复使用都会导致一个新的词汇表与它所构建的词汇表兼容。词汇表作者应该清楚地记录预期兼容性级别(如果有)。

元模式作者不应使用“$vocabulary”来组合定义同一关键字的冲突语法或语义的多个词汇表。由于语义冲突通常无法通过模式验证来检测,因此不希望实现检测到此类冲突。如果声明了冲突的词汇表,则结果行为未定义。

词汇表作者应该提供一个元模式来验证词汇表中关键字在自身上的预期用法。此类元模式不应禁止额外的关键字,并且不应禁止核心词汇表中的任何关键字。

建议元模式作者使用 "allOf" 关键字引用每个词汇表的元模式,尽管其他构建元模式的机制可能适合某些用例。

元模式的递归性质使得“$dynamicAnchor”和“$dynamicRef”关键字特别适用于扩展现有元模式,如 JSON 超级模式元模式扩展验证元模式的情况所示。

元模式可能会施加额外的约束,包括描述除了与声明的词汇表关联的元模式所描述的以外的关键字。这允许将用法限制在词汇表的一个子集中,并验证不打算重复使用的本地定义的关键字。

但是,元模式不应与它们声明的任何词汇表相矛盾,例如通过要求与词汇表期望的不同 JSON 类型。结果行为未定义。

旨在本地使用的元模式,不需要在任意实现中测试词汇表支持,可以安全地完全省略“$vocabulary”。

D.2. 带有词汇表声明的示例元模式

此元模式明确声明了核心词汇表和应用器词汇表,以及扩展词汇表,并将它们的元模式与“allOf”组合在一起。扩展词汇表的元模式(仅描述该词汇表中的关键字)显示在主示例元模式之后。

主示例元模式还通过禁止以“unevaluated”为前缀的关键字来限制未评估词汇表的用法,这些关键字在实现方面特别复杂。这不会改变其他词汇表定义的语义或关键字集。它只是确保使用此元模式的模式,如果尝试使用以“unevaluated”为前缀的关键字,将无法通过此元模式的验证。

最后,此元模式描述了一个名为“localKeyword”的关键字的语法,该关键字不属于任何词汇表。可以推测,此元模式的实施者和使用者会理解“localKeyword”的语义。JSON Schema 没有定义任何机制来表达词汇表之外的关键字语义,因此它们不适合使用,除非是在特定的环境中,其中它们被理解。

此元模式结合了多个词汇表以供通用。

{
  "$schema": "https://json-schema.fullstack.org.cn/draft/2020-12/schema",
  "$id": "https://example.com/meta/general-use-example",
  "$dynamicAnchor": "meta",
  "$vocabulary": {
    "https://json-schema.fullstack.org.cn/draft/2020-12/vocab/core": true,
    "https://json-schema.fullstack.org.cn/draft/2020-12/vocab/applicator": true,
    "https://json-schema.fullstack.org.cn/draft/2020-12/vocab/validation": true,
    "https://example.com/vocab/example-vocab": true
  },
  "allOf": [
    {"$ref": "https://json-schema.fullstack.org.cn/draft/2020-12/meta/core"},
    {"$ref": "https://json-schema.fullstack.org.cn/draft/2020-12/meta/applicator"},
    {"$ref": "https://json-schema.fullstack.org.cn/draft/2020-12/meta/validation"},
    {"$ref": "https://example.com/meta/example-vocab",
  ],
  "patternProperties": {
    "^unevaluated": false
  },
  "properties": {
    "localKeyword": {
      "$comment": "Not in vocabulary, but validated if used",
      "type": "string"
    }
  }
}

                    

此元模式仅描述单个扩展词汇表。

{
  "$schema": "https://json-schema.fullstack.org.cn/draft/2020-12/schema",
  "$id": "https://example.com/meta/example-vocab",
  "$dynamicAnchor": "meta",
  "$vocabulary": {
    "https://example.com/vocab/example-vocab": true,
  },
  "type": ["object", "boolean"],
  "properties": {
    "minDate": {
      "type": "string",
      "pattern": "\d\d\d\d-\d\d-\d\d",
      "format": "date",
    }
  }
}

                    

如上所示,即使通用元模式的“allOf”中引用的每个单词汇表元模式都声明了其对应的词汇表,此新的元模式也必须重新声明它们。

标准的元模式将 Core 和验证规范中定义的所有词汇表结合在一起,并结合了这些规范以及超链接模式规范中定义的所有词汇表,展示了其他复杂的组合。这些元模式的 URI 分别可在验证和超链接模式规范中找到。

虽然通用元模式可以验证“minDate”的语法,但它是词汇表定义了“minDate”语义含义背后的逻辑。如果没有对语义的理解(在本例中,即实例值必须等于或晚于作为关键字在模式中提供的日期),实现只能验证语法用法。在这种情况下,这意味着验证它是一个日期格式的字符串(使用“pattern”来确保即使“format”函数仅作为注释进行验证,如验证规范中所述)。

附录 E. 参考资料和生成用例

虽然引用存在与否预计对验证结果透明,但代码生成器和 UI 渲染器等生成用例通常将引用视为语义上的重要意义。

为了使这种用例特定的语义显式化,最佳做法是在与“$ref”之类的引用关键字相同的模式对象中创建注释关键字。

例如,这是一个用于确定代码生成器是否应该将引用目标视为一个单独的类以及这些类之间关系的假设关键字。请注意,此示例仅用于说明目的,不打算提出功能性代码生成关键字。

{
    "allOf": [
        {
            "classRelation": "is-a",
            "$ref": "classes/base.json"
        },
        {
            "$ref": "fields/common.json"
        }
    ],
    "properties": {
        "foo": {
            "classRelation": "has-a",
            "$ref": "classes/foo.json"
        },
        "date": {
            "$ref": "types/dateStruct.json",
        }
    }
}

                

在此,此模式表示某种面向对象的类。第一个“allOf”中的引用被标记为基类。第二个没有分配类关系,这意味着代码生成器应该将目标的定义与这个定义组合在一起,就好像没有引用一样。

查看属性,“foo”被标记为对象组合,而“date”属性没有。它只是一个包含子字段的字段,而不是一个单独类的实例。

这种类型的用法要求注释与引用位于同一对象中,该引用必须可以识别为引用。

附录 F. 致谢

感谢 Gary Court、Francis Galiegue、Kris Zyp 和 Geraint Luff 为 JSON Schema 的初始草案所做出的贡献。

感谢 Jason Desrosiers、Daniel Perrett、Erik Wilde、Evgeny Poberezkin、Brad Bowman、Gowry Sankar、Donald Pipowitch、Dave Finlay、Denis Laxalde、Phil Sturgeon、Shawn Silverman 和 Karen Etheridge 对文档的提交和补丁。

附录 G. 变更日志

[CREF13]此部分将在离开 Internet-Draft 状态之前删除。

draft-bhutton-json-schema-00

draft-handrews-json-schema-02

draft-handrews-json-schema-01

draft-handrews-json-schema-00

draft-wright-json-schema-01

draft-wright-json-schema-00

draft-zyp-json-schema-04

draft-zyp-json-schema-00

作者地址

Austin Wright (编辑) 电子邮件:[email protected]
Henry Andrews (编辑) 电子邮件:[email protected]
Ben Hutton (编辑) 电子邮件:[email protected] URI:https://jsonschema.dev
Greg Dennis 电子邮件:[email protected] URI:https://github.com/gregsdennis