Monaco-editor 的 JSON Schema 配置及使用介绍

JavaScript/前端
995
0
0
2023-03-14
目录
  • JSON Schema 作用用法
  • JSON Schema Core Vocabulary
  • $schema
  • $id
  • $ref
  • $dynamicRef
  • $anchor / $dynamicAnchor
  • $defs
  • 关键字
  • type
  • title & description
  • default
  • examples
  • deprecated
  • readOnly & writeOnly
  • $comment
  • enum
  • const
  • 媒体类型关键字
  • contentMediaType
  • contentEncoding
  • 组合模式
  • allOf
  • anyOf
  • oneOf
  • not
  • 子模式条件验证
  • if-then-else
  • dependentSchemas
  • dependentRequired
  • 数值验证
  • multipleOf
  • maximum
  • exclusiveMaximum
  • minimum
  • exclusiveMinimum
  • 字符串验证
  • maxLength
  • minLength
  • pattern
  • format
  • 数组验证
  • prefixItems
  • items
  • maxItems
  • minItems
  • uniqueItems
  • contains
  • maxContains / minContains
  • 对象验证
  • properties
  • patternProperties
  • additionalProperties
  • propertyNames
  • maxProperties
  • minProperties
  • required
  • Monaco Editor 的 JSON Schema 配置
  • 请求线上 json schema 配置文件
  • 配置
  • 用法
  • 自己编写 json schema 配置
  • 配置
  • 用法

JSON Schema 作用用法

jsonschema 是描述 JSON 数据的格式并提供验证结果,就好比 typescript 对于 JavaScript 的作用。

JSON Schema Core Vocabulary

$schema

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

该关键字的值必须是一个规范的URI。如果这个URI标识了一个可检索的资源,该资源的媒体类型应该是 application/schema+json。 

$schema 关键字应该在文档根模式对象中使用,并且可以在嵌入模式资源的根模式对象中使用。它绝对不能出现在非资源根模式对象中。如果文档根模式中没有,则产生的行为是由实现定义的。

$id

$id 关键字用它的规范URI标识一个模式资源,这个URI是一个标识符,而不一定是网络定位器。对于网络可寻址URL,不会从其规范URI下载。

$id 不能包含非空的片段,也不应该包含空的片段。绝对URI形式必须被认为是规范URI,无论是否存在空片段。当前允许使用空片段,因为旧的元模式的 $id (或以前的id)中有一个空片段。未来的草案可能会完全禁止 $id 中的空片段。

在子模式中出现 $id 表示子模式在单个模式文档中构成一个不同的模式资源。此外,如果子模式中的 $id 是一个相对URI引用,则解析该引用的基URI是父模式资源的URI。

如果没有父模式对象显式地将自己标识为具有 $id 的资源,则基URI就是整个文档的基URI。

$ref

$ref 关键字是一个应用程序,用于引用静态标识的模式。它的结果是引用模式的结果。注意,这种确定结果的定义意味着其他关键字可以与 $ref 一起出现在同一个模式对象中。

$ref 关键字的值必须是一个URI-Reference字符串,可以是相对当前模式内部地址,也可以是网络地址。它根据当前URI基进行解析,生成要应用的模式的URI。在模式加载时执行这种解析是安全的,因为计算实例的过程不能改变引用解析的方式。

$dynamicRef

$dynamicRef 关键字是一个应用程序,它允许将完整的解析延迟到运行时,在计算实例时,每次遇到它都会被解析。

$dynamicAnchor 一起,$dynamicRef 实现了一种协作扩展机制,它主要用于递归模式(引用自身的模式)。扩展点和运行时确定的扩展目标都是用 $dynamicAnchor 定义的,并且只有在使用 $dynamicRef 引用时才显示运行时动态行为。

$dynamicRef 属性的值必须是一个URI-Reference字符串。它根据当前URI基进行解析,生成用作运行时解析起点的URI。

如果最初解析的起点URI包含一个由 $dynamicAnchor 关键字创建的片段,则初始URI必须被动态作用域中最外层模式资源的URI(包括片段)所替换,该资源定义了一个与 $dynamicAnchor 同名的片段。

否则,它的行为与 $ref 相同,不需要运行时解析。

简单来说,$dynamicRef 就是为了运行时递归解析 $dynamicAnchor 所定义的模式。

$anchor / $dynamicAnchor

使用JSON指针片段需要了解模式的结构。当编写模式文档意图提供可重用的模式时,最好使用不绑定到任何特定结构位置的普通名称片段。这允许重新定位子模式,而不需要更新JSON指针引用。

$anchor$dynamicAnchor 关键字用于指定这样的片段。它们是标识符关键字,只能用于创建普通名称片段,而不是像 $id 那样的绝对uri。

除非需要指定 $dynamicAnchor,否则就使用 $anchor

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

$defs

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

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

例如,下面是一个描述正整数数组的模式,其中正整数约束是 $defs 中的子模式:

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

关键字

JSON Schema包含一些关键字,它们不是严格用于验证,而是用于描述模式的各个部分。这些“注释”关键字都不是必需的,但是作为良好的实践,鼓励使用它们,并且可以使您的模式“自文档化”。

type

该关键字的值必须是字符串或数组。如果它是一个数组,数组的元素必须是字符串并且必须是唯一的。 字符串值必须是六种基本类型(nullbooleanobjectarraynumber,或 String )中的一种,或者是匹配任何小数部分为零的数字的 integer。 如果 type 的值是一个字符串,那么如果实例的类型与该字符串值所表示的类型匹配,则实例验证成功。如果 type 的值是一个数组,那么实例验证成功的前提是它的类型与所指示的任何类型相匹配

title & description

这两个关键字的值必须是一个字符串。

这两个关键字都可以用来用该用户界面产生的数据信息装饰用户界面。标题最好是简短的,而描述将解释此模式所描述的实例的目的。

default

default 关键字指定一个默认值。此值不用于在验证过程中填充缺失的值。文档生成器或表单生成器等非验证工具可能使用此值向用户提示如何使用该值。但是,default 通常用于表示如果缺少一个值,那么该值在语义上与使用默认值时的值相同。default 的值应该根据它所在的模式进行验证,但这不是必需的。

examples

该关键字的值必须是一个数组。对数组中的值没有任何限制。

该关键字可用于提供与特定模式相关联的示例JSON值,以便演示使用方法。建议这些值对关联的模式有效。

如果存在,实现可以使用 default 值作为额外的示例。如果没有 examplesdefault 仍然可以以这种方式使用。证,但这不是严格要求的。不需要复制 examples 数组中的默认值,因为 default 将被视为另一个示例。

deprecated

deprecated 关键字是一个布尔值,表示该关键字所应用的实例值不应被使用,将来可能会被删除。

readOnly & writeOnly

布尔型关键字 readOnlywriteOnly 通常在API上下文中使用。

readOnly 表示不修改该值。它可以用来表示更改值的PUT请求将导致400 Bad request响应。

writeOnly 表示可以设置一个值,但将保持隐藏。In可以用来表示可以用PUT请求设置一个值,但是在用GET请求检索该记录时不包括它。

$comment

$comment 关键字严格用于向模式添加注释。它的值必须总是一个字符串。与注释标题、描述和示例不同,JSON模式实现不允许为其附加任何含义或行为,甚至可以随时剥离它们。因此,它们用于给JSON模式的未来编辑器留下注释,但不应用于与模式的用户通信。

enum

enum 关键字用于将一个值限制为一组固定的值。它必须是一个至少有一个元素的数组,其中每个元素都是唯一的。

如果 enum 数组的值包含该属性的值,则对该关键字验证成功。

const

const 关键字用于将一个值限制为单个值。

该关键字的值可以是任何类型,等同于只有一个值的 enum

如果实例的值等于该关键字的值,则实例验证成功。

媒体类型关键字

{
    "type": "string",
    "contentEncoding": "base64",
    "contentMediaType": "image/png"
}

contentMediaType

如果实例是字符串,则此属性指示字符串内容的媒体类型。如果存在 contentEncoding,则此属性描述已解码的字符串。

该属性的值必须是一个字符串,必须是一个媒体类型。

contentEncoding

此属性的值必须是一个字符串。

如果实例值是字符串,此属性定义该字符串应被解释为编码的二进制数据,并使用此属性命名的编码进行解码。

可接受的值是 7bit, 8bit, binary, quote-printable, base16, base32base64。如果未指定,则编码与包含JSON文档的编码相同。

组合模式

allOf

该关键字的值必须是非空数组。数组的每一项必须是有效的JSON Schema。

如果实例成功地验证了由该关键字的值定义的所有模式,则该实例就成功地验证了该关键字。

// 示例
{
  "reg": {
    "type": "string",
    "allOf": [
      { "maxLength": 10 },
      { "minLength": 3 }
    ]
  }
}

maxLength 和 minLength 都需要满足才验证成功。

anyOf

该关键字的值必须是非空数组。数组的每一项必须是有效的JSON Schema。

如果实例成功地验证了由该关键字的值定义的至少一个模式,则该实例就成功地验证了该关键字。注意,在收集注释时,必须检查所有子模式,以便从每个成功验证的子模式收集注释。

// 示例
{
  "reg": {
    "anyOf": [
      { "type": "number", "minimum": 20 },
      { "type": "string", "minLength": 3 }
    ]
  }
}

type & minimum 和 type & minLength 两个条件至少满足一个即验证成功。

oneOf

该关键字的值必须是非空数组。数组的每一项必须是有效的JSON Schema。

如果一个实例仅对由该关键字的值定义的一个模式进行了成功验证,则该实例对该关键字进行了成功验证。

// 示例
{
  "reg": {
    "type": "string",
    "oneOf": [
      { "maxLength": 10 },
      { "minLength": 3 }
    ]
  }
}

maxLength 和 minLength 只能满足其中一个才能验证成功。

not

该关键字的值必须是一个有效的JSON模式。

如果一个实例对该关键字定义的模式验证失败,则该实例对该关键字有效。

// 示例
{
  "reg": {
    "type": "string",
    "not": { "minLength": 3 }
  }
}

不能满足 minLength 才能验证成功。

子模式条件验证

if-then-else

ifthenelse 这三个关键字必须是有效的 JSON Schema。

如果 if 模式验证成立,则使用 then 模式,失败则使用 else 模式。

如果 if 不存在,则 thenelse 关键字不起作用。

{
  "$schema": "http://json-schema.org/draft-07/schema",
  "description": "test json schema",
  "type": "object",
  "title": "Schema",
  "properties": {
    "foo": { "type": "string" },
    "bar1": { "type": "string" },
    "bar2": { "type": "string" }
  },
  "if": {
    "properties": {
      "foo": { "const": "123" }
    }
  },
  "then": {
    "required": [ "bar1" ]
  },
  "else": {
    "required": [ "bar2" ]
  }
}

以上 schema,如果 foo 的值为 '123',则 bar1 是必须存在的,否则 bar2 是必须存在的。

dependentSchemas

dependentSchemas 关键字在给定属性出现时有条件地应用子模式。该模式的应用方式与 allOf 应用模式相同。没有合并或扩展任何内容。两个模式都独立应用。

{
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "credit_card": { "type": "number" }
  },
  "required": ["name"],
  "dependentSchemas": {
    "credit_card": {
      "properties": {
        "billing_address": { "type": "string" }
      },
      "required": ["billing_address"]
    }
  }
}

以上示例,如果 credit_card 存在,则 billing_address 也必须以字符串存在。

dependentRequired

该关键字的值必须是一个对象。此对象中的属性(如果有)必须是数组。每个数组中的元素(如果有的话)必须是字符串,并且必须是唯一的。

该关键字指定在出现特定其他属性时所需的属性。他们的要求取决于其他属性的存在。

如果对于实例中出现的每个名称以及该关键字值中作为名称出现的每个名称,对应数组中的每个项也是实例中属性的名称,则验证成功。

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

{
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "credit_card": { "type": "number" },
    "billing_address": { "type": "string" }
  },
  "required": ["name"],
  "dependentRequired": {
    "credit_card": ["billing_address"]
  }
}

数值验证

multipleOf

multipleOf 的值必须是一个数字,且必须严格大于0。

数值实例只有在除以该关键字的值得到整数时才有效

maximum

maximum 的值必须是一个数字,表示数值实例的包含上限。

如果实例是一个数字,则此关键字仅在实例小于或恰好等于 maximum 时验证成功。

exclusiveMaximum

exclusivemmaximum 的值必须是一个数字,表示数值实例的排他上限。

如果实例是一个数字,那么该实例只有在其值严格小于(不等于)时才有效。

minimum

minimum 的值必须是一个数字,表示数值实例的包含下限。

如果实例是一个数字,则此关键字仅在实例大于或恰好等于 minimum 时验证成功。

exclusiveMinimum

exclusivminimum 的值必须是一个数字,表示数值实例的排他下限。

如果实例是一个数字,那么只有当它的值严格大于(不等于)时,该实例才有效。

字符串验证

maxLength

该关键字的值必须是非负整数。

如果字符串实例的长度小于或等于该关键字的值,则该字符串实例对该关键字有效。

minLength

该关键字的值必须是非负整数。

如果字符串实例的长度大于或等于该关键字的值,则该字符串实例对该关键字有效。

省略这个关键字与值为0的行为相同

pattern

该关键字的值必须为字符串。该字符串应该是一个有效的正则表达式。

如果正则表达式成功匹配字符串实例,则认为该字符串实例有效。

{
  "regex": {
    "type": "string",
    "pattern": "^\\d+$"
  }
}

format

format关键字允许对常用的某些类型的字符串值进行基本语义标识。例如,因为JSON没有“DateTime”类型,所以需要将日期编码为字符串。Format允许模式作者指出应该将字符串值解释为日期。默认情况下,format只是一个注释,不影响验证。

{
  "date": {
    "type": "string",
    "format": "date"
  }
}

format 支持的类型: built-in-formats

数组验证

prefixItems

prefixItems 的值必须是一个有效的JSON schema的非空数组,主要用于对元组的校验。

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

该关键字产生的注释值是该关键字应用子模式的最大索引。如果子模式应用于实例的每个索引,例如由 items 键产生,则该值可能为布尔值true

{
  "type": "array",
  "prefixItems": [
    { "type": "number" },
    { "type": "string" },
    { "enum": ["Street", "Avenue", "Boulevard"] },
    { "enum": ["NW", "NE", "SW", "SE"] }
  ]
}

如果同级的 items 关键字的值为 false,则该关键字的实例值不可超出 prefixItems 的长度。

items

该关键字可以是 array or object or boolean,用来定义数组内元素的类型。

array:不能为空,可以为每个位置的元素定义模式或者实例,如果为空对象或者后面元素未定义则为任意类型。这里的数组类型虽然文档上面没有提到,但是测试下来,如果没有其它的需求,与 prefixItems 的效果一样。

object:可以使用 allOfanyOf 等批量定义模式

boolean:true 为任意类型,false 则数组为空

省略这个关键字与值为true的行为相同

maxItems

该关键字的值必须是非负整数。

如果数组长度的大小小于或等于此关键字的值,则该数组实例对 maxItems 有效。

minItems

该关键字的值必须是非负整数。

如果数组长度的大小大于或等于该关键字的值,则该数组实例对 minItems 有效。

省略这个关键字与值为0的行为相同

uniqueItems

该关键字的值必须为布尔值。

如果该关键字的布尔值为false,则实例验证成功。如果它的布尔值为true,则实例成功验证其所有元素是否唯一。

省略这个关键字与false值具有相同的行为

contains

包含模式只需要对数组中的一个或多个项进行验证。

{
   "type": "array",
   "contains": { "type": "number" }
}
// ["life", "universe", "everything", 42] // 有效

maxContains / minContains

minContainsmaxContains 可以与 contains 一起使用,以进一步指定模式匹配包含约束的次数。这些关键字可以是包括零在内的任何非负数。

{
   "type": "array",
   "contains": { "type": "number" },
   "minContains": 2,
   "maxContains": 3
}
// ["life", "universe", "everything", 42] // 无效
// ["life", "universe", "everything", 42, 21] // 有效
// ["life", "universe", "everything", 42, 21, 12, 1] // 无效

对象验证

properties

对象上的属性(键-值对)是使用 properties 关键字定义的。属性的值是一个对象,其中每个键是属性的名称,每个值是用于验证该属性的模式。任何不匹配 properties 关键字中的任何属性名称的属性都将被此关键字忽略。

{
  "type": "object",
  "properties": {
    "number": { "type": "number" },
    "street_name": { "type": "string" },
    "street_type": { "enum": ["Street", "Avenue", "Boulevard"] }
  }
}

patternProperties

有时给定特定类型的属性名,值应该匹配特定的模式。这就是 patternProperties 发挥作用的地方:它将正则表达式映射到模式。如果属性名与给定的正则表达式匹配,则属性值必须根据相应的模式进行验证。

{
  "type": "object",
  "patternProperties": {
    "^S_": { "type": "string" },
    "^I_": { "type": "integer" }
  }
}
// { "S_25": "This is a string" } // 有效
// { "S_0": 42 } // 无效

additionalProperties

additionalProperties 关键字用于控制额外内容的处理,也就是说,名称没有在 properties 关键字中列出或匹配 patternProperties 关键字中的任何正则表达式的属性。默认允许任何附加属性。

additionalProperties 关键字的值是一个模式,将用于验证实例中未被 properties或patternProperties 匹配的任何属性。将 additionalProperties 模式设置为false意味着不允许使用其他属性。

{
  "type": "object",
  "properties": {
    "number": { "type": "number" }
  },
  "additionalProperties": false
}
// { "number": 1600 } // 有效
// { "number": 1600, "street_name": "Pennsylvania" } // 无效

可以使用非布尔模式在实例的附加属性上添加更复杂的约束。例如,可以允许附加的值都是字符串的属性:

{
  "type": "object",
  "properties": {
    "number": { "type": "number" }
  },
  "additionalProperties": { "type": "string" }
}

propertyNames

属性的名称可以根据模式进行验证,而不管其值如何。如果您不想强制执行特定的属性,但希望确保这些属性的名称遵循特定的约定,那么这可能很有用。例如,您可能希望强制所有名称都是有效的ASCII令牌,以便它们可以在特定的编程语言中用作属性。

{
  "type": "object",
  "propertyNames": {
    "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
  }
}
// { "_a_proper_token_001": "value" } // 有效

maxProperties

该关键字的值必须是非负整数。

如果对象实例的属性数量小于或等于该关键字的值,则对象实例针对 maxProperties 是有效的。

minProperties

该关键字的值必须是非负整数。

如果对象实例的属性数量大于或等于该关键字的值,则对象实例针对 minProperties 是有效的。

省略这个关键字与值为0的行为相同

required

该关键字的值必须是一个数组。这个数组的元素(如果有的话)必须是字符串,并且必须是唯一的。

如果数组中的每个项都是实例中的属性名称,则对象实例针对此关键字有效。

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

Monaco Editor 的 JSON Schema 配置

setDiagnosticsOptions 该 API 就是在 monaco editor 内来配置 json shcema 的,下面就是简单的使用示例。

import * as monaco from 'monaco-editor'
interface DiagnosticsOptions {
  /**
   * If set, the validator will be enabled and perform syntax and schema based validation,
   * unless `DiagnosticsOptions.schemaValidation` is set to `ignore`.
   */
  readonly validate?: boolean;
  /**
   * If set, comments are tolerated. If set to false, syntax errors will be emitted for comments.
   * `DiagnosticsOptions.allowComments` will override this setting.
   */
  readonly allowComments?: boolean;
  /**
   * A list of known schemas and/or associations of schemas to file names.
   */
  readonly schemas?: {
      /**
       * The URI of the schema, which is also the identifier of the schema.
       */
      readonly uri: string;
      /**
       * A list of glob patterns that describe for which file URIs the JSON schema will be used.
       * '*' and '**' wildcards are supported. Exclusion patterns start with '!'.
       * For example '*.schema.json', 'package.json', '!foo*.schema.json', 'foo/**\/BADRESP.json'.
       * A match succeeds when there is at least one pattern matching and last matching pattern does not start with '!'.
       */
      readonly fileMatch?: string[];
      /**
       * The schema for the given URI.
       */
      readonly schema?: any;
  }[];
  /**
   *  If set, the schema service would load schema content on-demand with 'fetch' if available
   */
  readonly enableSchemaRequest?: boolean;
  /**
   * The severity of problems from schema validation. If set to 'ignore', schema validation will be skipped. If not set, 'warning' is used.
   */
  readonly schemaValidation?: SeverityLevel;
  /**
   * The severity of problems that occurred when resolving and loading schemas. If set to 'ignore', schema resolving problems are not reported. If not set, 'warning' is used.
   */
  readonly schemaRequest?: SeverityLevel;
  /**
   * The severity of reported trailing commas. If not set, trailing commas will be reported as errors.
   */
  readonly trailingCommas?: SeverityLevel;
  /**
   * The severity of reported comments. If not set, 'DiagnosticsOptions.allowComments' defines whether comments are ignored or reported as errors.
   */
  readonly comments?: SeverityLevel;
}
monaco.languages.json.jsonDefaults.setDiagnosticsOptions(options: DiagnosticsOptions)

API 对于 schema 有两种配置方式,一种是请求线上的,一种是本地配置。

这里只记录关键的几个 API 的作用。

请求线上 json schema 配置文件

配置

monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
  validate: true,
  enableSchemaRequest: true
})

字段:

  • validate:检验 json
  • enableSchemaRequest:启用 schema 请求

用法

如上图,当前 json 配置了一个线上的 $schema 地址,在启动的时候,浏览器会自动的发起 GET 请求,Monaco 加载后来校验 json 数据。

自己编写 json schema 配置

配置

monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
  validate: true,
  schemas: [
    {
      uri: 'custom-uri',
      schema: {
        type: 'object',
        description: 'sheet tag of the tax project',
        properties: {
          param: {
            description: 'param 1',
            type: 'string'
          },
          name: {
            description: 'name  description',
            type: 'string'
          },
          prices: {
            description: 'price of the goods',
            type: 'number',
            exclusiveMinimum: 0
          }
        },
        required: [
          'param',
          'name',
          'prices'
        ]
      }
    }
  ]
})

字段:

  • validate: 检验 json
  • schemas: 多套 schema 配置
  • uri: 可以把它看成是当前 schema 配置的锚点
  • schema: json schema 的配置数据

用法

这里要注意的是,当前 json 内 $schema 的值必须与上面配置内的 uri 匹配,否则会不生效。多套 json 数据格式,在使用的时候我们只需要修改 $schema 的值即可。

参考

json-schema.org/

monaco-editor json shcema