Skip to content

JSON Schema 全景漫游 #282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
yanyue404 opened this issue Sep 25, 2024 · 0 comments
Open

JSON Schema 全景漫游 #282

yanyue404 opened this issue Sep 25, 2024 · 0 comments

Comments

@yanyue404
Copy link
Owner

yanyue404 commented Sep 25, 2024

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": "jhon.doe@MyCompany.com",
    "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 yanyue404 changed the title A tour of JSON Schema JSON Schema 全景漫游 Sep 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant