blog icon indicating copy to clipboard operation
blog copied to clipboard

JSON Schema 全景漫游

Open yanyue404 opened this issue 5 months ago • 0 comments

site: https://tour.json-schema.org/ repo: https://github.com/json-schema-org/tour

01-Getting-Started

01-Your-First-Schema

{
  "name": "John Doe",
  "age": 25
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    }
  }
}

02-Nesting-Objects

嵌套对象

{
  "name": {
    "firstName": "John",
    "lastName": "Doe",
    "middleName": "Smith"
  }
  "age": 25,
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "object",
      "properties": {
        "firstName": {
          "type": "string"
        },
        "lastName": {
          "type": "string"
        },
        "middleName": {
          "type": "string"
        }
      }
    },
    "age": {
      "type": "integer"
    }
  }
}

03-Required-Properties

{
  "name": {
    "firstName": "John",
    "lastName": "Doe",
    "middleName": "Smith"
  }
  "age": 25,
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "object",
      "properties": {
        "firstName": {
          "type": "string"
        },
        "lastName": {
          "type": "string"
        },
        "middleName": {
          "type": "string"
        }
      },
      "required": ["firstName", "lastName"]
    },
    "age": {
      "type": "integer"
    }
  }
}

04-Enumerated-Values

{
  "name": "John Doe",
  "age": 25,
  "hobbies": "reading"
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "hobbies": {
      "type": "string",
      "enum": ["reading", "writing", "painting"]
    }
  }
}

05-Arrays

{
  "name": "John Doe",
  "age": 25,
  "hobbies": ["reading", "writing"]
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "hobbies": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": ["name", "age", "hobbies"]
}

06-Array-of-Objects

{
  "name": "John Doe",
  "age": 25,
  "skills": [
    {
      "name": "JavaScript",
      "level": "beginner"
    },
    {
      "name": "React",
      "level": "intermediate"
    }
  ]
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "skills": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": { "type": "string" },
          "level": { "type": "string" }
        }
      }
    }
  }
}

02-Primitive-Types

01-Constraining-String-Length

{
  "name": "John Doe",
  "age": 25,
  "postalCode": "385004",
  "phoneNumber": "84584898564"
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "phoneNumber": {
      "type": "string",
      "minLength": 10,
      "maxLength": 10
    },
    "postalCode": {
      "type": "string",
      "minLength": 6,
      "maxLength": 6
    }
  }
}

02-Regular-Expressions-in-Strings

{
  "name": "John Doe",
  "age": 25,
  "postalCode": "385004",
  "phoneNumber": "84584898564",
  "countryCode": "IN"
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "postalCode": {
      "type": "string",
      "pattern": "^[0-9]{6}$"
    },
    "phoneNumber": {
      "type": "string",
      "pattern": "^[0-9]{10}$"
    },
    "countryCode": {
      "type": "string",
      "pattern": "^[A-Z]{2}$"
    }
  }
}
  • ^ asserts the start of the string.
  • [0-9] matches any digit from 0 to 9.
  • {6} specifies that the previous pattern should be repeated exactly 6 times.
  • $ asserts the end of the string.

03-Constraining-Number

{
  "name": "John Doe ",
  "age": 25,
  "dateOfBirth": {
    "day": 1,
    "month": 1,
    "year": 1995
  }
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer",
      "minimum": 18,
      "maximum": 60
    },
    "dateOfBirth": {
      "type": "object",
      "properties": {
        "year": {
          "type": "integer",
          "minimum": 1964,
          "maximum": 2024
        },
        "month": {
          "type": "integer",
          "minimum": 1,
          "maximum": 12
        },
        "day": {
          "type": "integer",
          "minimum": 1,
          "maximum": 31
        }
      }
    }
  }
}

integer 表示整数类型

04-Exclusively-Constraining-Number

{
  "name": "John Doe",
  "age": 25,
  "salary": 50000
}
  • age should be greater than 18 and less than 60
  • salary should be greater than 30000 and less than 80000
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer",
      "exclusiveMinimum": 18,
      "exclusiveMaximum": 60
    },
    "salary": {
      "type": "integer",
      "exclusiveMinimum": 30000,
      "exclusiveMaximum": 80000
    }
  }
}

exclusiveMinimum 表示 x 大于某值,x > exclusiveMinimum

exclusiveMaximum 表示 x 小于某值, x < exclusiveMaximum

05-Multiple-of-a-Number

{
  "name": "John Doe",
  "age": 25,
  "hourlyWage": 10
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "hourlyWage": {
      "type": "number",
      "multipleOf": 0.25,
      "minimum": 0,
      "maximum": 100
    }
  }
}

hourlyWage 是 0.25 的倍数,且 hourlyWage 最小值为 0 ,最大值为 100

06-Decimal-Numbers

{
  "name": "John Doe",
  "age": 25,
  "performanceRating": 4.5
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "performanceRating": {
      "type": "number",
      "minimum": 0,
      "maximum": 5
    }
  }
}

PerformanceRating 属性接受最小值为 0 、最大值为 5 的十进制数字, 可以是小数

07-Enumerated Values II

{
  "name": "John Doe",
  "age": 25,
  "performanceRating": 4
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "performanceRating": {
      "enum": [1, 2, 3, 4, 5, null]
    }
  }
}

以前,我们只对字符串使用枚举值。但是,您也可以将它们用于任何类型,包括数字和空值。

08-Defining-Constant-Values

{
  "name": "John Doe",
  "age": 25,
  "companyName": "MyCompany"
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": { "type": "integer", "const": 25 },
    "companyName": {
      "const": "MyCompany"
    }
  }
}
  • const 关键字用于为属性定义单个常量值。const 关键字的值可以是任何有效的 JSON 值。

companyName 是一个常量值 MyCompany, age 为常量 25

09-Combining-Types

{
  "name": "John Doe",
  "age": 25,
  "hasAgreedToTerms": true
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "hasAgreedToTerms": {
      "type": ["boolean", "null"]
    }
  }
}

可以通过将类型数组传递给 type 字段来定义多个类型

hasAgreedToTerms 属性接受布尔值和空值

03-Objects

01-Pattern-Properties

{
  "name": "John Doe",
  "age": 25,
  "DEPT-001": "HR"
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    }
  },
  "patternProperties": {
    "^DEPT-[0-9]{3}$": {
      "pattern": "^[A-Z]+$"
    }
  },
  "additionalProperties": false
}

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

提示: 使用 patternProperties 关键字定义属性名称的模式,使用 pattern 关键字定义属性值的模式。您可以将模式设置为 ^[ A-Z ]+$ 以匹配所有大写字母。

addtionalProperties 设置为 false 以禁止任何其他属性。我们将在下一课中学习更多关于附加属性的知识。

02-Additional-Properties

{
  "type": "object",
  "properties": {
    "name": {...},
    "age": {...}
  },
  "additionalProperties": false
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    }
  },
  "additionalProperties": {
    "type": "integer"
  }
}

默认情况下,允许任何附加属性。如果将 additionalProperties 设置为 false 可以禁止任何附加属性。

可以将 addtionalProperties 设置为一个 schema,以为其他属性定义 schema

03-Constraining-Number-of-Properties

{
  "name": "John Doe",
  "age": 25,
  "contactMethods": {
    "email": "[email protected]",
    "phone": "1234567890",
    "mobile": "0987654321"
  }
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "contactMethods": {
      "type": "object",
      "minProperties": 2,
      "maxProperties": 5,
      "additionalProperties": {
        "type": "string"
      }
    }
  }
}

现在,将 minProperties 和 maxProperties 关键字添加到侧编辑器上的模式中,以设置 contactMethods 对象中属性的最小和最大数量。

此外,在 contactMethod 对象中,使用 addtionalProperties 关键字确保属性的值是字符串类型。

04-Applying-Schema-to-Property-Names

{
  "type": "object",
  "propertyNames": {
    "pattern": "^[A-Z]+$"
  }
}
  1. 对象应该至少有 2 个属性。

  2. 属性名称应以大写字母书写,并且长度应至少为 3 个字符。

  3. 属性值应该是字符串或整数。

{
  "type": "object",
  "minProperties": 2,
  "propertyNames": {
    "pattern": "^[A-Z]{3,}$"
  },
  "additionalProperties": {
    "type": ["string", "integer"]
  }
}

04-Arrays

01-Specifying-Length-of-an-Array

{
  "name": "John Doe",
  "age": 25,
  "phones": ["123-456-7890", "987-654-3210"]
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "phones": {
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^\\d{3}-\\d{3}-\\d{4}$"
      },
      "minItems": 1,
      "maxItems": 3
    }
  }
}

phones 属性,并将最小项指定为 1,最大项指定为 3。另外,将电话号码的格式限制为 xxx-xxx-xxxx。

提示:使用正则表达式^\\d{3}-\\d{3}-\\d{4}$pattern 关键字来验证电话号码格式。

02-Unique-Items

{
  "name": "John Doe",
  "age": 25,
  "phones": ["123-456-7890", "987-654-3210"]
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "phones": {
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^\\d{3}-\\d{3}-\\d{4}$"
      },
      "uniqueItems": true
    }
  }
}

03-Tuple-Validation

{
  "name": "John Doe",
  "age": 25,
  "address": [123, "Main St", "Avenue", "NW"]
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "address": {
      "type": "array",
      "prefixItems": [
        { "type": "integer" },
        { "type": "string" },
        { "type": "string", "enum": ["Street", "Avenue", "Boulevard"] },
        { "type": "string", "enum": ["NW", "NE", "SE", "SW"] }
      ]
    }
  }
}

在这种情况下,可以使用 prefixItems 关键字为元组数组的每个元素定义模式。

Example

{
  "type": "array",
  "prefixItems": [{ "type": "integer" }, { "type": "string" }]
}

上面的模式定义了一个数组,其中第一个元素应该是整数,第二个元素应该是字符串。

04-Additional-Items-in-Tuples

{
  "name": "John Doe",
  "age": 25,
  "address": [123, "Main St", "Avenue", "NW"]
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "address": {
      "type": "array",
      "items": {
        "type": ["string"]
      },
      "prefixItems": [
        {
          "type": "number"
        },
        {
          "type": "string"
        },
        {
          "enum": ["Street", "Avenue", "Boulevard"]
        },
        {
          "enum": ["NW", "NE", "SW", "SE"]
        }
      ]
    }
  }
}

可以将 items 关键字设置为 false,以禁止在元组数组中添加其他项.

提示:向 items 关键字添加一个子 schame,以定义附加项的 schema。

05-Enumerated-Array-Items

{
  "name": "John Doe",
  "age": 25,
  "relevantDepartments": ["HR", "Finance"]
}
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "relevantDepartments": {
      "type": "array",
      "items": {
        "enum": ["HR", "Finance", "IT", "Admin"]
      },
      "uniqueItems": true
    }
  }
}

前面,我们使用 enum 来限制字符串属性的值。类似地,可以使用 enum 关键字限制数组项的值。

06-Ensuring-Array-Content-With-Contains

在下面的示例中,我们有一个验证数字数组的 JSON Schema。该模式确保数组至少包含一个小于 10 的元素。

{
  "type": "array",
  "contains": {
    "type": "number",
    "maximum": 10
  }
}

现在,我们要确保 skills 数组至少包含一个等于“JavaScript”的元素。

提示:使用 const 关键字指定 JavaScript 作为必需的元素。

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "skills": {
      "type": "array",
      "contains": {
        "type": "string",
        "const": "JavaScript"
      }
    }
  },
  "required": ["name", "age", "skills"]
}

07-minContains-and-maxContains

下面的示例演示了如何使用 minContains 和 maxContains 关键字来确保数组恰好包含 2 个小于 10 的元素。

{
  "type": "array",
  "contains": {
    "type": "number",
    "maximum": 10
  },
  "minContains": 2,
  "maxContains": 2
}

我们的员工有工作时间。我们要确保 workedHours 数组至少包含两个大于或等于 8 且小于或等于 12 的元素。

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "workedHours": {
      "type": "array",
      "contains": {
        "type": "number",
        "minimum": 8,
        "maximum": 12
      },
      "minContains": 2
    }
  }
}

08-Unevaluated-Items

我们已经定义了带有 prefixItems 的数组项。当项目多于定义的 prefixItems 时,可以使用 unevaluatedItems 为其余项目定义子模式。

{
  "type": "array",
  "prefixItems": [{ "type": "number" }, { "type": "string" }],
  "unevaluatedItems": { "type": "number" }
}

在这种情况下,前两个项由 prefixItems 计算,其余项由 unevaluatedItems 计算。

Task

  • 前三个元素只能有“HTML”、“CSS”和“JavaScript”作为值(以任何顺序),不能有其他任何值。

  • 其余元素为字符串类型。

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "skills": {
      "type": "array",
      "prefixItems": [
        { "type": "string", "enum": ["HTML", "CSS", "JavaScript"] },
        { "type": "string", "enum": ["HTML", "CSS", "JavaScript"] },
        { "type": "string", "enum": ["HTML", "CSS", "JavaScript"] }
      ],
      "unevaluatedItems": { "type": "string" }
    }
  }
}

05-Conditional-Validation

01-Ensuring-Conditional-Property-Presence

什么是条件验证?

条件验证涉及根据 JSON 文档中其他属性的存在与否应用验证规则。

例如:

  • dependentRequired: 如果属性 a 存在,那么属性 B 必须存在。
  • dependentSchemas: 如果存在属性 a,则应用子模式 B。
  • if-then-else: 如果子模式 a 有效,则子模式 B 必须有效,否则子模式 C 必须有效。
  • implications: 如果子模式 A 是有效的,那么子模式 B 一定是有效的。
{
  "name": "John Doe",
  "creditCardNumber": "1234 5678 1234 5678",
  "address": "123 Main St"
}

Task

如果 creditCardNumber 属性存在,那么地址属性也必须存在。

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "creditCardNumber": {
      "type": "string"
    },
    "address": {
      "type": "string"
    }
  },
  "dependentRequired": {
    "creditCardNumber": ["address"]
  },
  "required": ["name"]
}

02-Mutual Dependency

{
  "name": "John Doe",
  "creditCardNumber": "1234 5678 1234 5678",
  "address": "123 Main St"
}

确保 creditCardNumber 和 address 是相互依赖的。

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "creditCardNumber": {
      "type": "string"
    },
    "address": {
      "type": "string"
    }
  },
  "dependentRequired": {
    "creditCardNumber": ["address"],
    "address": ["creditCardNumber"]
  },
  "required": ["name"]
}

03-Conditionally-Apply-a-Subschema

当你希望基于属性的存在与否应用 subschema 时,可以使用 dependentSchemas 关键字。此关键字允许您定义仅在存在或不存在特定属性时应用的 subschema。

Task

{
  "name": "John Doe",
  "creditCardNumber": "1234 5678 1234 5678",
  "address": "123 Main St"
}

更新以确保如果存在 creditCardNumber 属性,那么使用 dependentSchemas 关键字也必须存在地址属性。

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "creditCardNumber": {
      "type": "string"
    }
  },
  "dependentSchemas": {
    "creditCardNumber": {
      "properties": {
        "address": {
          "type": "string"
        }
      },
      "required": ["address"]
    }
  },
  "required": ["name"]
}

04-if-then-keyword

{
  "name": "John Doe",
  "isStudent": true,
  "age": 25
}

如果 isStudent 为真,则年龄字段必须存在。

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "isStudent": {
      "type": "boolean"
    }
  },
  "required": ["name"],
  "if": {
    "properties": {
      "isStudent": {
        "const": true
      }
    },
    "required": ["isStudent"]
  },
  "then": {
    "required": ["age"]
  }
}

05-if-then-else

{
  "name": "John Doe",
  "isStudent": true,
  "age": 25
}

如果 isStudent 为真,则必须显示年龄字段,否则必须显示年级字段。

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "isStudent": {
      "type": "boolean"
    },
    "grade": {
      "type": "number",
      "minimum": 0,
      "maximum": 10
    }
  },
  "if": {
    "properties": { "isStudent": { "const": true } },
    "required": ["isStudent"]
  },
  "then": { "required": ["age"] },
  "else": { "required": ["grade"] },
  "required": ["name", "isStudent"]
}

06-Combining-Subschemas

01-Reusing-and-Referencing-with-defs-and-ref

组合子模式

在本模块中,您将学习如何组合多个子模式来创建更复杂的 JSON 模式。您将学习以下关键字:allOf、anyOf、oneOf、not、$defs、$ref 和递归模式。

在 JSON Schema 中,$defs关键字允许您定义可重用的子模式。然后可以使用$ref 关键字引用这些子模式。

要用$defs 定义一个子模式,你可以使用以下语法:

{
  "$defs": {
    "mySubschema": {
      "type": "string",
      "maxLength": 10
    }
  }
}

在上面的例子中,我们定义了一个名为 mySubschema 的子模式,它指定该值应该是一个最大长度为 10 个字符的字符串。

要使用$ref 来引用这个子模式,你可以使用以下语法:

{
  "$ref": "#/$defs/mySubschema"
}

在上面的例子中,我们使用$ref 来引用 mySubschema 子模式。这允许我们在需要时重用 mySubschema 的定义。

通过促进代码重用,使用$defs$ref 可以帮助您的 JSON 模式更加模块化和可维护。

Task

{
  "name": {
    "firstName": "John",
    "middleName": "Smith",
    "lastName": "Doe"
  }
}

在 schame 中定义名为 stringType 的子模式,它只是“type”:“string”,然后使用$ref 引用属性 firstName, middleName 和 lastName

{
  "$defs": {
    "stringType": {
      "type": "string"
    }
  },
  "type": "object",
  "properties": {
    "name": {
      "type": "object",
      "properties": {
        "firstName": {
          "$ref": "#/$defs/stringType"
        },
        "middleName": {
          "$ref": "#/$defs/stringType"
        },
        "lastName": {
          "$ref": "#/$defs/stringType"
        }
      }
    }
  },
  "required": ["name"]
}

02-id-and-schema

$schema 在 JSON Schema 中, $schema 是一个字符串,定义了写入该 schema 的 JSON Schema 标准的版本。

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "string"
}

在上面的示例中,架构是在 JSON Schema 草案 2020-12 中编写的。您可以在此处找到最新版本的 JSON Schema。

在本教程中,我们使用了 JSON Schema 草案 2020-12。

$id

$id 关键字是一个字符串,用于定义模式的 URI。

{
  "$id": "https://example.com/person",
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    }
  }
}

在上面的示例中,模式的 URI 为https://example.com/person

模式 URI 可用于使用 $ref关键字从其他模式引用该模式。

{
  "$ref": "https://example.com/person"
}

Task

我们定义了一个简单的模式,如下所示。

{
  "$id": "https://example.com/string",
  "type": "string"
}

在 schema 中使用 $ref 关键字在 name 属性中引用此模式。

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "name": {
      "$ref": "https://example.com/string"
    },
    "age": {
      "type": "integer"
    }
  }
}

03-Valid-Against-allOf-the-Subschemas(AND)

对所有子 schema(AND)有效

当您希望确保一个实例对所有子模式都有效时,可以使用 allOf 关键字。

您可以将 allOf 视为子模式之间的 AND 操作。如果其中一个子模式失败,则认为该实例无效。

{
  "name": "John Doe"
}

我们将创建两个子模式 1. minStringLength2. 字母和数字,然后使用 name 属性中的 allOf 将它们组合起来。

{
  "$defs": {
    "minStringLength": { "minLength": 5 },
    "alphaNumeric": { "pattern": "^[a-zA-Z0-9]*$" }
  },
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "allOf": [
        { "$ref": "#/$defs/minStringLength" },
        { "$ref": "#/$defs/alphaNumeric" }
      ]
    }
  }
}

上面的模式确保 name 属性对 minStringLength 和 alphannumeric 子模式都有效。换句话说,name 属性的最小长度必须为 5 个字符,并且必须只包含字母数字字符。

注意:没有必要使用$defs$ref 来定义子 schame。您也可以直接内联地定义子 schema。

Task

定义一个内联子模式,检查 age 是否为整数类型

使用 age 属性中的 allOf 组合 ageLimit 和内联子模式。

{
  "$defs": {
    "ageLimit": {
      "minimum": 18,
      "maximum": 60
    }
  },
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "allOf": [
        {
          "type": "integer"
        },
        {
          "$ref": "#/$defs/ageLimit"
        }
      ]
    }
  },
  "required": ["name"]
}

04-Valid-Against-oneOf-the-Subschemas(XOR)

验证对其中一个子 schema 有效

Example

{
  "name": "John Doe",
  "age": 25,
  "dateOfBirth": "1999-01-01"
}

让我们向 JSON 文档添加一个新的属性 dateOfBirth:

现在要在文档中应用这些条件:

  • 如果存在年龄,则不应存在出生日期,反之亦然。
  • 年龄和出生日期不应同时出现。
  • 如果它们都不存在,那么该文档应该是无效的。
{
  "type": "object",
  "properties": {
    ...
    "dateOfBirth": { "type": "string", "format": "date" }
  },
  "oneOf": [
    { "required": ["age"] },
    { "required": ["dateOfBirth"] }
  ]
}

Task

{
  "name": "John Doe",
  "age": 25
}

在侧面编辑器中为上面的 JSON 文档提供了一个模式。使用 oneOf 关键字,更新模式以确保年龄符合以下条件之一:

  • 年龄介乎 18 至 60 岁(包括在内) ,或,
  • 大于 65(含)。

提示: 使用最小 (minimum) 和最大关键字 (maximum) 来定义范围。

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer",
      "oneOf": [{ "minimum": 18, "maximum": 60 }, { "minimum": 65 }]
    }
  }
}

05-Valid-Against-anyOf-the-Subschemas(OR)

对任何子模式有效 (OR)

anyOf 关键字用于定义多个子模式。如果该实例对于任一子模式有效,则该文档被视为有效。

Example

{
  "name": "John Doe",
  "age": 25,
  "dateOfBirth": "1995-12-17"
}

我们希望在文档中允许使用“age”和“dateOfBirth”属性。如果文档具有 Age 或 dateOfBirth 属性,则该文档有效。我们可以使用 anyOf 关键字来定义这个条件。

{
  "type": "object",
  "properties": {...},
  "anyOf": [
    {"required": ["age"]},
    {"required": ["dateOfBirth"]}
  ]
}

Task

{
  "name": "John Doe",
  "employeeType": "full-time",
  "salary": 50000,
  "hourlyRate": 25
}

要求:

  • 它具有 salary 或 hourlyRate 属性。
  • 它具有 salary 和 hourlyRate 属性。
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "employeeType": {
      "enum": ["full-time", "part-time"]
    }
  },
  "anyOf": [
    {
      "required": ["salary"]
    },
    {
      "required": ["hourlyRate"]
    }
  ]
}

06-inverting-validation-with-not

反向验证

not关键字用于反转验证。如果实例对于子模式无效,则该文档被视为有效。

{
  "name": "John Doe",
  "age": 25
}

我们希望年龄大于或等于 18 岁。但不应该是 21 岁。我们可以使用 not 关键字来定义此条件。

{
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "age": {
      "type": "integer",
      "minimum": 18,
      "not": { "const": 21 }
    }
  }
}

Task

{
  "name": "John Doe",
  "age": 21,
  "status": "active"
}

要求:

  • status 不能为 null
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    },
    "status": {
      "not": {
        "type": "null"
      }
    }
  }
}

07-Recursive-Schemas

递归架构

在 JSON 模式中,递归模式允许定义引用自身的模式。这在处理分层数据结构或建模递归关系时非常有用。

{
  "name": "John Doe",
  "links": [
    {
      "name": "Jane Doe",
      "links": [
        {
          "name": "Alice Doe",
          "links": []
        }
      ]
    },
    {
      "name": "Jack Doe",
      "links": []
    }
  ]
}

要定义递归模式,可以使用 $ref 关键字引用模式本身:

{
  "$defs": {
    "TreeNode": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": { "type": "string" },
          "children": { "$ref": "#/$defs/TreeNode" }
        },
        "required": ["name", "children"]
      }
    }
  },
  "$ref": "#/$defs/TreeNode"
}

在上面的示例中,TreeNode 架构在 children 属性中引用自己,从而创建一个递归结构。

Task

{
  "name": "John Doe",
  "next": {
    "value": "Jane Doe",
    "next": {
      "value": "Alice Doe",
      "next": {}
    }
  }
}

使用递归模式定义 next schema:

  • 在 $defs 内部定义一个子模式,接下来引用它自己并将 null 作为基本情况。
  • 您将在 $defs/next 模式中定义 next 属性。next property 的值可以是 null,也可以是下一个模式本身。您可以使用 anyOf 关键字来定义多个模式。
{
  "$defs": {
    "next": {
      "type": "object",
      "properties": {
        "value": {
          "type": ["string"]
        },
        "next": {
          "oneOf": [
            {
              "$ref": "#/$defs/next"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": ["next", "value"]
    }
  },
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "next": {
      "$ref": "#/$defs/next"
    }
  }
}

07-Miscellaneous

01-Extending-Closed-Schemas-with-unevaluatedProperties

扩展封闭的 schame

在前面的 Objects 模块中,我们学习了 addtionalProperties。但是,需要注意的是,addtionalProperties 只能识别在同一个子模式中声明的属性。

因此,addtionalProperties 可以限制您使用组合关键字(如 allOf)“扩展”模式。在下面的示例中,我们可以看到 addtionalProperties 如何导致扩展地址架构示例的尝试失败。

{
  "allOf": [
    {
      "type": "object",
      "properties": {
        "street_address": { "type": "string" },
        "city": { "type": "string" },
        "state": { "type": "string" }
      },
      "required": ["street_address", "city", "state"],
      "additionalProperties": false
    }
  ],
  "properties": {
    "type": { "enum": ["residential", "business"] }
  },
  "required": ["type"]
}

上述架构不允许您定义类型属性。因为 addtionalProperties 被设置为 false。原因是,addtionalProperties 只能识别在同一个子模式中声明的属性。

Unevaluated Properties

我们在 addtionalProperties 中看到的挑战可以使用 unevaluatedProperties 关键字来解决。此关键字允许您定义不由当前架构计算的属性。

{
  "allOf": [
    {
      "type": "object",
      "properties": {
        "street_address": { "type": "string" },
        "city": { "type": "string" },
        "state": { "type": "string" }
      },
      "required": ["street_address", "city", "state"]
    }
  ],
  "properties": {
    "type": { "enum": ["residential", "business"] }
  },
  "unevaluatedProperties": false,
  "required": ["type"]
}

Task

将 unevaluatedProperties 添加到架构中,以允许将数字 number作为附加属性。

{
  "allOf": [
    {
      "type": "object",
      "properties": {
        "street_address": {
          "type": "string"
        },
        "city": {
          "type": "string"
        },
        "state": {
          "type": "string"
        }
      },
      "required": ["street_address", "city", "state"]
    }
  ],
  "properties": {
    "type": {
      "enum": ["residential", "business"]
    }
  },
  "required": ["type"],
  "unevaluatedProperties": {
    "type": "number"
  }
}

08-Annotating-JSON-Schemas

01-Title-and-Description

title 和 description 关键字用于提供人类可读的模式标题和描述。

这些关键字不用于验证,但工具和软件可以使用它们来提供关于模式的更多信息。

{
  "type": "object",
  "title": "Person",
  "description": "A person in the system",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "integer"
    }
  }
}

02-deprecated-readOnly-and-writeOnly

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

布尔关键字 readOnly 和 writeOnly 通常用于 API 上下文中。

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

  • writeOnly 表示可以设置一个值,但将保持隐藏。它可用于指示您可以使用 PUT 请求设置一个值,但在使用 GET 请求检索该记录时不会包含它。

Task

  • 向 schema 添加deprecated弃用关键字。
  • 将 schema 中的 readOnly 添加到 name 属性中
  • 向 age 属性添加 writeOnly。
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "readOnly": true
    },
    "age": {
      "type": "integer",
      "writeOnly": true
    }
  },
  "deprecated": true
}

03-comment-and-default

要在 JSON Schema 中添加注释,请使用$comment关键字。$comment 关键字是一个字符串,它提供了关于模式的附加信息

可以使用 default 关键字为属性设置默认值。default 关键字用于为属性提供默认值。

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "$comment": "This is the name of the employee"
    },
    "age": {
      "type": "integer",
      "default": 18
    }
  }
}

yanyue404 avatar Sep 25 '24 11:09 yanyue404