使用JSON-Schema验证数据,第1部分

处理复杂的结构化数据时,需要确定数据是否有效。 JSON-Schema是JSON文档的标准,描述了JSON数据的结构和要求。 在这个由两部分组成的系列文章中,您将学习如何使用JSON-Schema来验证数据。

假设您有一个用户数据库,其中每个记录看起来都类似于此示例:

{
  "id": 64209690,
  "name": "Jane Smith",
  "email": "jane.smith@gmail.com",
  "phone": "07777 888 999",
  "address": {
    "street": "Flat 1, 188 High Street Kensington",
    "postcode": "W8 5AA",
    "city": "London",
    "country": "United Kingdom"
  },
  "personal": {
    "DOB": "1982-08-16",
    "age": 33,
    "gender": "female"
  },
  "connections": [
    {
      "id": "35434004285760",
      "name": "John Doe",
      "connType": "friend",
      "since": "2014-03-25"
    },
    {
      "id": 13418315,
      "name": "James Smith",
      "connType": "relative",
      "relation": "husband",
      "since": "2012-07-03"
    }
  ],
  "feeds": {
    "news": true,
    "sport": true,
    "fashion": false
  },
  "createdAt": "2015-09-22T10:30:06.000Z"
}

我们要处理的问题是如何确定上述记录是否有效。

示例非常有用,但在描述数据要求时还不够。 JSON-Schema可以解决。 这是描述用户记录的可能模式之一:

{
  "$schema": "https://json-schema.org/draft-04/schema#",
  "id": "http://mynet.com/schemas/user.json#",
  "title": "User",
  "description": "User profile with connections",
  "type": "object",
  "properties": {
    "id": {
      "description": "positive integer or string of digits",
      "type": ["string", "integer"],
      "pattern": "^[1-9][0-9]*$",
      "minimum": 1
    },
    "name": { "type": "string", "maxLength": 128 },
    "email": { "type": "string", "format": "email" },
    "phone": { "type": "string", "pattern": "^[0-9()\-\.\s]+$" }, 
    "address": {
      "type": "object",
      "additionalProperties": { "type": "string" },
      "maxProperties": 6,
      "required": ["street", "postcode", "city", "country"]
    },
    "personal": {
      "type": "object",
      "properties": {
        "DOB": { "type": "string", "format": "date" },
        "age": { "type": "integer", "minimum": 13 },
        "gender": { "enum": ["female", "male"] }
      }
      "required": ["DOB", "age"],
      "additionalProperties": false
    },
    "connections": {
      "type": "array",
      "maxItems": 150,
      "items": {
        "title": "Connection",
        "description": "User connection schema",
        "type": "object",
        "properties": {
          "id": {
            "type": ["string", "integer"],
            "pattern": "^[1-9][0-9]*$",
            "minimum": 1
          },
          "name": { "type": "string", "maxLength": 128 },
          "since": { "type": "string", "format": "date" },
          "connType": { "type": "string" },
          "relation": {},
          "close": {}
        },
        "oneOf": [
          {
            "properties": {
              "connType": { "enum": ["relative"] },
              "relation": { "type": "string" }
            },
            "dependencies": {
              "relation": ["close"]
            }
          },
          {
            "properties": {
              "connType": { "enum": ["friend", "colleague", "other"] },
              "relation": { "not": {} },
              "close": { "not": {} }
            }
          }
        ],
        "required": ["id", "name", "since", "connType"],
        "additionalProperties": false
      }
    },
    "feeds": {
      "title": "feeds",
      "description": "Feeds user subscribes to",
      "type": "object",
      "patternProperties": {
        "^[A-Za-z]+$": { "type": "boolean" }
      },
      "additionalProperties": false
    },
    "createdAt": { "type": "string", "format": "date-time" }
  }
}

查看上面的架构及其描述的用户记录(根据该架构有效)。 这里有很多解释要做。

用于根据模式验证用户记录JavaScript代码可以是:

var Ajv = require('ajv');
var ajv = Ajv({allErrors: true});
var valid = ajv.validate(userSchema, userData);
if (valid) {
  console.log('User data is valid');
} else {
  console.log('User data is INVALID!');
  console.log(ajv.errors);
}

或为了获得更好的性能:

var validate = ajv.compile(userSchema);
var valid = validate(userData);
if (!valid) console.log(validate.errors);

GitHub repo tutsplus-json-schema中提供了所有代码示例。 您也可以在浏览器中尝试一下

示例中使用的验证器Ajv是JavaScript最快的JSON-Schema验证器。 我创建了它,因此在本教程中将使用它。

在继续之前,让我们快速处理所有原因。

为什么要单独验证数据?

  • 快速失败
  • 避免数据损坏
  • 简化处理代码
  • 在测试中使用验证码

为什么要使用JSON(而不是XML)?

  • 与XML一样广泛
  • 比XML更容易处理和更简洁
  • 由于JavaScript主导了Web开发

为什么要使用架构?

  • 陈述式
  • 易于维护
  • 可以被非编码者理解
  • 无需编写代码,可以使用第三方开源库

为什么要使用JSON模式?

  • 在所有JSON验证标准中采用最广泛
  • 非常成熟(当前版本为4,对于版本5有建议)
  • 涵盖验证场景的很大一部分
  • 使用易于解析的JSON文档进行模式
  • 平台无关
  • 容易扩展
  • 30种以上用于不同语言的验证器,其中10种以上用于JavaScript,因此无需自己编写代码

任务

本教程包括几个相对简单的任务,以帮助您更好地了解JSON模式以及如何使用它。 有简单JavaScript脚本可以检查您是否正确完成了脚本。 要运行它们,您将需要安装node.js(您不需要任何经验)。 只需安装nvm (节点版本管理器)和最新的node.js版本:

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.29.0/install.sh | bash
nvm install 4

您还需要克隆存储库并运行npm install (它将安装Ajv验证程序)。

让我们深入了解模式!

JSON模式始终是一个对象。 它的属性称为“关键字”。 其中一些描述数据的规则(例如,“类型”和“属性”),一些描述模式本身(“ $ schema”,“ id”,“ title”,“ description”),我们将介绍他们以后。

如果数据根据该模式中的所有关键字均有效,则根据该模式是有效的-这确实很简单。

资料属性

因为大多数JSON数据由具有多个属性的对象组成,所以关键字“ properties”可能是最常用的关键字。 它仅适用于对象(有关“应用”的含义,请参见下一节)。

在上面的示例中,您可能已经注意到, “ properties”关键字内的每个属性都描述了数据中的相应属性。

每个属性的值本身就是JSON模式-JSON模式是一种递归标准。 数据中的每个属性都应根据“ properties”关键字中的相应架构有效。

这里重要的是, “ properties”关键字不需要任何属性。 它仅定义数据中存在的属性的模式。

例如,如果我们的模式是:

{
  "properties": {
    "foo": { "type": "string" }
  }
}

那么根据此模式,具有或不具有属性“ foo”的对象都可以有效:

{foo: "bar"}, {foo: "bar", baz: 1}, {baz: 1}, {} // all valid

并且只有属性foo不是字符串的对象才是无效的:

{ foo: 1 } // invalid

在浏览器中尝试此示例

数据类型

您已经弄清楚了关键字“ type”的作用。 它可能是最重要的关键字。 它的值(字符串或字符串数​​组)定义数据必须有效的类型。

如上例所示,用户数据必须是一个对象。

大多数关键字适用于某些数据类型,例如,关键字“属性”仅适用于对象,关键字“模式”仅适用于字符串。

“应用”是什么意思? 假设我们有一个非常简单的架构:

{ "pattern": "^[0-9]+$" }

您可能希望根据这样的模式有效,数据必须是与模式匹配的字符串:

"12345"

但是JSON模式的标准规定,如果某个关键字不适用于该数据类型,则根据该关键字数据是有效的。 这意味着根据上面的模式(数字,数组,对象,布尔值,甚至为null),任何非“字符串”类型的数据均有效。 如果只希望与模式匹配的字符串有效,则架构应为:

{
  "type": "string",
  "pattern": "^[0-9]+$"
}

因此,您可以创建非常灵活的架构来验证多种数据类型。

在用户示例中查看属性“ id”。 根据以下架构,它应该是有效的:

{
  "type": ["string", "integer"],
  "pattern": "^[1-9][0-9]*$",
  "minimum": 1
}

此架构要求有效的数据应该是“字符串”“整数” 。 还有关键字“ pattern”仅适用于字符串。 它要求字符串应仅由数字组成,而不应从0开头。关键字“ minimum”仅适用于数字; 它要求该数字不少于1。

表达相同要求的另一种更为冗长的方法是:

{
  "anyOf": [
    { "type": "string", "pattern": "^[1-9][0-9]*$" },
    { "type": "integer", "minimum": 1 },
  ]
}

但是由于定义了JSON模式,该模式与第一个模式等效,在大多数验证程序中,该模式更短,更快。

可以在模式中使用的数据类型是“对象”“数组”“数字”“整数”“字符串”“布尔”“空” 。 请注意, “数字”包括“整数”,所有整数也都是数字。

号码验证

有几个关键字可以验证数字。 本节中的所有关键字仅适用于数字(包括整数)。

“最小”“最大”是不言自明的。 除它们之外,还有关键字“ exclusiveMinimum”“ exclusiveMaximum” 。 在我们的用户示例中,用户年龄必须是13或更大的整数。 如果用户年龄的架构为:

{
  "type": "integer",
  "minimum": 13,
  "exclusiveMinimum": true
}

那么此架构将要求用户年龄严格大于13,即允许的最低年龄为14。

验证数字的另一个关键字是“ multipleOf” 。 它的名称也说明了它的作用,您可以查看JSON-schema关键字参考以了解其工作原理。

字符串验证

还有几个关键字可以验证字符串。 本节中的所有关键字仅适用于字符串。

“ maxLength”“ minLength”要求字符串不长于或短于给定的数字。 JSON模式的标准要求将unicode对(例如emoji字符)计为单个字符。 当您访问字符串的.length属性时,JavaScript会将其视为两个字符。

一些验证器根据标准要求确定字符串长度,而另一些验证器以JavaScript方式执行,这更快。 Ajv允许您指定如何确定字符串长度,默认设置是符合标准。

var schema = { "maxLength": 2 };

var ajv = Ajv(); // count unicode pairs as one character
ajv.validate(schema, "??"); // true
ajv.validate(schema, "??!"); // false

var ajv = Ajv({unicode: false}); // count unicode pairs as two characters
ajv.validate(schema, "?"); // true
ajv.validate(schema, "?!"); // false

您已经在使用“ pattern”关键字了,它只需要数据字符串与根据JavaScript中使用的相同标准定义的正则表达式匹配即可。 有关架构和匹配的正则表达式,请参见下面的示例:

{"pattern": "^[1-9][0-9]*$" };   /^[1-9][0-9]*$/;  // id
{"pattern": "^[A-Za-z]+$" };     /^[a-z]+$/i;      // letters
{"pattern": "^[0-9()\-\.\s]+$"}; /^[0-9()\-\.\s]+$/; // phone

“ format”关键字定义字符串的语义验证,例如在用户示例中为“ email”“ date”“ date-time” 。 JSON-Schema还定义了格式“ uri”“主机名”“ ipv4”“ ipv6” 。 验证器以不同的方式定义格式,以优化验证速度或正确性。 Ajv给您一个选择:

var ajv = Ajv(); // "fast" format validation
ajv.validate({"format": "date"}, "2015-12-24"); // true
ajv.validate({"format": "date"}, "2015-14-33"); // true

var ajv = Ajv({format: 'full'); // more thorough format validation
ajv.validate({"format": "date"}, "2015-12-24"); // true
ajv.validate({"format": "date"}, "2015-14-33"); // false

大多数验证器允许您将自定义格式定义为正则表达式或验证函数。 我们可以为我们的模式定义一个自定义格式“ phone” ,以便在多个地方使用它:

ajv.addFormat('phone', /^[0-9()\-\.\s]+$/);

然后phone属性的架构为:

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

任务1

创建一个架构,该架构将要求数据为日期(字符串)或年份(数字),并且年份大于或等于1976。

将答案放在文件part1/task1/date_schema.json然后运行node part1/task1/validate进行检查。

对象验证

除了“属性”之外 ,您还可以在我们的用户示例中看到适用于对象的其他几个关键字。

“ required”关键字列出了对象中必须存在的属性才能使其有效。 您还记得, “ properties”关键字不需要属性,它仅在存在属性时对其进行验证。 “必需”“属性 ”的补充,使您可以定义哪些属性是必需的,哪些是可选的。

如果我们有这个模式:

{
  "properties": {
    "foo": { "type": "string" }
  },
  "required": ["foo"]
}

那么所有没有属性foo对象都将无效。

请注意,此架构仍然不需要将我们的数据作为对象-所有其他数据类型根据它都是有效的。 要要求我们的数据是一个对象,我们必须在其中添加“ type”关键字。

在浏览器中尝试上面的示例

关键字“ patternProperties”允许您定义模式,如果属性名称与正则表达式匹配,则数据属性值应根据该模式有效。 可以在同一模式中将其与“ properties”关键字结合使用。

根据以下模式,用户示例中的feeds属性应该有效:

{
  "type": "object",
  "patternProperties": {
     "^[A-Za-z]+$": { "type": "boolean" }
  },
  "additionalProperties": false
}

为了有效, feeds应该是一个对象,该对象的名称仅由拉丁字母组成,并且其值为布尔值。

关键字“ additionalProperties”允许您根据所有其他关键字(未在“属性”中使用且与“ patternProperties”不匹配 )有效的方式来定义架构,或者完全禁止其他属性,如在feeds属性中所做的那样上面的模式。

在以下示例中, “ additionalProperties”用于为特定范围内的整数哈希创建简单的架构:

var schema = {
  "type": "object",
  "additionalProperties" {
    "type": "integer",
    "minimum": 0,
    "maximum": 65535
  }
};
var validate = ajv.compile(schema);
validate({a:1,b:10,c:100});    // true
validate({d:1,e:10,f:100000}); // false
validate({g:1,h:10,i:10.5});   // false
validate({j:1,k:10,l:'abc'});  // false

使用“ maxProperties”“ minProperties”关键字可以限制对象中的属性数量。 在我们的用户示例中, address属性的架构为:

{
  "type": "object",
  "additionalProperties": { "type": "string" },
  "maxProperties": 6,
  "required": ["street", "postcode", "city", "country"]
}

此架构要求地址是具有必需属性streetpostcodecitycountry ,允许两个附加属性(“ maxProperties”为6),并且要求所有属性均为字符串。

“依赖项”可能是最复杂,最令人困惑且使用最罕见的关键字,但同时又是一个非常强大的关键字。 它允许您定义数据具有某些属性时应满足的要求。

对对象的这种要求有两种类型:具有其他一些属性(称为“属性依赖性”)或满足某种模式(“模式依赖性”)。

在我们的用户示例中,用户连接应针对的可能模式之一是:

{
  "properties": {
    "connType": { "enum": ["relative"] },
    "relation": { "type": "string" }
  },
  "dependencies": {
    "relation": ["close"]
  }
}

它要求connType属性等于“相对”(见下面的“枚举”关键字),并且如果该relation属性存在时,它是一个字符串。

它不要求存在relation ,但是“ dependencies”关键字要求如果存在relation属性,则也应该存在close属性。

尽管从示例中我们可以看到它可能必须为布尔值,但是在我们的模式中没有为close属性定义验证规则。 我们可以纠正此遗漏的方法之一是将“ dependencies”关键字更改为使用“ schemadependent”:

"dependencies": {
  "relation": {
    "properties": {
      "close": { "type": "boolean" },
    },
    "required": ["close"]
  }
}

您可以在浏览器中播放更新的用户示例

请注意, “ dependencies”关键字中“ relation”属性中的模式用于验证父对象(即连接),而不是数据中的relation属性的值。

任务2

您的数据库包含人员和机器。 仅使用到目前为止已经解释过的关键字创建一个架构来验证它们。 样本人类对象:

{
  "human": true,
  "name": "Jane",
  "gender": "female",
  "DOB": "1985-08-12"
}

样本机器对象:

{
  "model": "TX1000",
  "made": "2013-08-29"
}

请注意,它应该是同时验证人和机器的一种模式,而不是两种模式。

将答案放入文件part1/task2/human_machine_schema.json然后运行node part1/task2/validate进行检查。

提示:使用“ dependencies”关键字,然后在文件part1/task2/invalid.json查找哪些对象应该无效。

哪些对象可能也应该无效,也不在invalid.json文件中?

此任务的主要收获是,验证的目的不仅仅是验证所有有效对象是否有效。 我已经多次听到这种说法:“此架构将我的有效数据验证为有效,因此是正确的。” 该参数是错误的,因为您不需要做太多的事情就可以实现—空模式可以完成任务,因为它可以验证任何数据是否有效。

我认为验证的主要目的是将无效数据验证为无效数据,这就是所有复杂性的来源。

阵列验证

有几个关键字可以验证数组(它们仅适用于数组)。

“ maxItems”“ minItems”要求数组的项目数不超过(或不少于)。 在用户示例中,该架构要求连接数不超过150。

“ items”关键字定义了一个(或多个)模式,项目应根据该模式有效。 如果此关键字的值是一个对象(如用户示例中所示),则此对象是一个模式,根据该模式数据应该有效。

如果“ items”关键字的值是一个数组,则此数组包含相应的项目应根据其有效的架构:

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

上面简单示例中的模式要求数据是数组,第一项是整数,第二项是字符串。

那两个之后的项目呢? 上面的架构没有定义其他项目的要求。 可以使用“ additionalItems”关键字定义它们。

“ additionalItems”关键字仅适用于“ items”关键字是数组且数据中的项目“ items”关键字多的情况。 在所有其他情况下(没有“ items”关键字,它是一个对象,或者数据中没有更多的项目), “ additionalItems”关键字将被忽略,无论其值如何。

如果“ additionalItems”关键字为true,则将其忽略。 如果为假,并且数据中的项目多于“ items”关键字,则验证失败:

var schema = {
  "type": "array",
  "items": [
    { "type": "integer" },
    { "type": "string" }
  ],
  "additionalItems": false
};

var validate = ajv.compile(schema);
console.log(validate([1, "foo", 3])); // false

如果关键字“ additionalItems”是一个对象,则此对象是一个架构,根据该架构,所有其他项均应有效:

var schema = {
  "type": "array",
  "items": [
    { "type": "integer" },
    { "type": "string" }
  ],
  "additionalItems": { "type": "integer" }
};

var validate = ajv.compile(schema);
console.log(validate([1, "foo", 3])); // true
console.log(validate([1, "foo", 3, 4])); // true
console.log(validate([1, "foo", "bar"])); // false

尝试这些示例,以了解“项目”“ additionalItems”的工作方式。

适用于数组的最后一个关键字是“ uniqueItems” 。 如果其值为true ,则仅要求数组中的所有项目都不同。

验证关键字“ uniqueItems”在计算上可能是昂贵的,因此某些验证器选择不实施或仅部分实施。

Ajv可以选择忽略此关键字:

var schema = {
  "type": "array",
  "uniqueItems": true
};

var ajv = Ajv(); // validate uniqueItems
ajv.validate(schema, [1, 2, 3]); // true
ajv.validate(schema, [1, 2, 2]); // false

var ajv = Ajv({uniqueItems: false}); // ignore uniqueItems
ajv.validate(schema, [1, 2, 3]); // true
ajv.validate(schema, [1, 2, 2]); // true

任务3

在JavaScript中创建日期对象的一种方法是将2到7个数字传递给Date构造函数:

var date = new Date(2015, 2, 15); // Sun Mar 15 2015 00:00:00 GMT+0000, month is 0 based
var date2 = new Date(year, month0, day, hour, minute, seconds, ms);

您有一个数组。 创建一个架构,该架构将验证这是Date构造函数的有效参数列表。

将您的答案放在文件part1/task3/date_args_schema.json然后运行node part1/task3/validate进行检查。

“枚举”关键字

“ enum”关键字要求数据等于多个值之一。 它适用于所有类型的数据。

在用户示例中,它用于将personal财产内的gender财产定义为“男性”或“女性”。 它还用于在用户连接中定义connType属性。

尽管不是很普遍, “ enum”关键字可以与任何类型的值一起使用,不仅可以用于字符串和数字。

也可以用来要求数据等于特定值,如用户示例中所示:

"properties": {
  "connType": { "enum": ["relative"] },
  ...
}

JSON-Schema标准的下一版本 (v5) 的建议包括关键字“ constant”以执行相同的操作。

Ajv允许您使用v5中的“ constant”和其他一些关键字:

var schema = {
  "constant": "relative"
};

var ajv = Ajv({v5: true}); // this options enables v5 keywords
var validate = ajv.compile(schema);
validate("relative"); // true
validate("other"); // false

复合验证关键字

有几个关键字使您可以定义涉及针对多个模式进行验证的高级逻辑。 本节中的所有关键字均适用于所有数据类型。

我们的用户示例使用“ oneOf”关键字来定义对用户连接的要求。 如果数据成功针对数组内的一个模式成功验证,则此关键字有效。

如果数据根据“ oneOf”关键字中的所有模式无效,或者根据两个或多个模式有效,则该数据无效。

让我们更仔细地看一下我们的示例:

{
  ...
  "oneOf": [
    {
      "properties": {
        "connType": { "enum": ["relative"] },
        "relation": { "type": "string" }
      },
      "dependencies": {
        "relation": ["close"]
      }
    },
    {
      "properties": {
        "connType": { "enum": ["friend", "colleague", "other"] },
        "relation": { "not": {} },
        "close": { "not": {} }
      }
    }
  ],
  ...
}

上面的模式要求用户连接要么是“相对的”( connType属性),在这种情况下,它可能具有“ relation (字符串)和“ close (布尔值)属性,或者是“朋友”,“同事”或“其他”类型之一(在这种情况下,它一定不能具有属性relationclose )。

这些用于用户连接的模式是互斥的,因为没有数据可以同时满足这两个条件。 因此,如果连接有效且类型为“相对”,则没有必要根据第二个模式对其进行验证-连接将始终无效。 但是,任何验证器都将始终根据两种模式验证数据,以确保仅根据一种模式有效。

还有另一个关键字可以避免: “ anyOf” 。 此关键字仅要求数据根据数组中的某些模式(可能对几种模式)有效。

在上述情况下,架构是互斥的,并且根据一个以上的架构,没有任何数据可以有效,因此最好使用“ anyOf”关键字-在大多数情况下(除了其中的一种),它的验证速度更快。数据根据最后一个模式有效)。

“ anyOf”表现出色的情况下使用“ oneOf”是一个非常常见的错误,会对验证性能产生负面影响。

我们的用户示例还将受益于将“ oneOf”替换为“ anyOf”

但是,在某些情况下,当我们确实需要“ oneOf”关键字时:

{
  "type": "string",
  "oneOf": [
    { "pattern": "apple" }
    { "pattern": "orange" }
  ]
}

上面的模式将成功地验证提到橙子或苹果的字符串,但不能同时验证两种字符串(并且确实存在可以同时提及这两种字符串)。 如果这是您需要的,则需要使用“ oneOf”

与布尔运算符相比, “ anyOf”就像布尔OR, “ oneOf”就像XOR(异或)。 JavaScript(和许多其他语言)没有为“异或”定义运算符的事实表明,很少需要它。

还有关键字“ allOf” 。 它要求数据根据数组中的所有模式都是有效的:

{
  "allOf": [
    { "$ref": "http://mynet.com/schemas/feed.json#" },
    { "maxProperties": 5 }
  ]
}

“ $ ref”关键字允许您根据另一个文件(或文件的某些部分)中的架构要求数据有效。 我们将在本教程的第二部分中进行研究。

另一个错误是在模式中的“ oneOf”“ anyOf”“ allOf”关键字中添加了绝对必要的内容。 例如,在我们的用户示例中,我们可以将连接应满足的所有要求放入“ anyOf”中

我们也可能不必要地使苹果和橙子的例子变得复杂:

{
  "oneOf": [
    { "type": "string", "pattern": "apple" }
    { "type": "string", "pattern": "orange" }
  ]
}

另一个“逻辑”关键字是“ not” 。 它要求根据该关键字的值的模式,数据无效。

例如:

{
  "type": "string",
  "not": {
    "pattern": "apple"
  }
}

上面的模式将要求数据是不包含“ apple”的字符串。

在用户示例中, “ not”关键字用于防止某些属性在“ oneOf”中的一种情况下使用,尽管它们已定义:

{
  "properties": {
    "connType": { "enum": ["friend", "colleague", "other"] },
    "relation": { "not": {} },
    "close": { "not": {} }
  }
}

上例中的“ not”关键字的值是一个空模式。 空模式将验证任何值是否有效,而“ not”关键字将使其无效。 因此,如果对象具有属性relationclose则架构验证将失败。 您可以结合使用“ not”“ required”关键字来实现相同的目的。

关键字“ not”的另一种用法是定义模式,该模式要求数组包含根据某些模式有效的项目:

{
  "not": {
    "items": {
      "not: {
        "type": "integer",
        "minimum": 5
      }
    }
  }
}

上面的模式要求数据是一个数组,并且包含至少一个大于或等于5的整数项。

V5提案包含关键字“ contains”来满足此要求。

var schema = {
  "type": "array",
  "contains": {
    "type": "integer",
    "minimum": 5
  }
};

var ajv = Ajv({v5: true}); // enables v5 keywords
var validate = ajv.compile(schema);
validate([3, 4, 5]); // true
validate([1, 2, 3]); // false

任务4

您有一个用户数据库,这些数据库都与该用户示例中的模式匹配。 创建一个架构,根据该架构,只有满足所有这些条件的用户才有效:

  • 未满21岁或未满60岁的未婚男性
  • 具有5个或更少的连接
  • 订阅3个或更少的提要

将您的答案放在文件part1/task4/filter_schema.json然后运行node part1/task4/validate进行检查。

测试数据经过简化,因此请不要在架构中使用“ required”关键字。

描述架构的关键字

用户示例中使用的某些关键字不会直接影响验证,但它们描述了架构本身。

“ $ schema”关键字定义该模式的元模式的URI。 模式本身是一个JSON文档,可以使用JSON模式进行验证。 定义任何JSON模式的JSON模式称为元模式。 JSON-schema标准草案4的元模式的URI是http://json-schema.org/draft-04/schema

如果扩展标准,建议您使用“ $ schema”属性的其他值。

“ id”是架构URI。 它可以用于使用“ $ ref”关键字从另一个架构引用该架构(或其一部分),请参阅本教程的第二部分。 包括Ajv在内的大多数验证器都允许将任何字符串用作“ id” 。 根据标准,架构ID应该是可用于下载架构的有效URI。

您还可以使用“标题”“描述”来描述架构。 验证期间不使用它们。 这两个关键字都可以在模式内部的任何级别上使用,以描述模式的某些部分,如在用户示例中所做的那样。

最后任务

创建用户记录的示例,该示例在通过示例用户架构进行验证时将出现8个或更多错误。

将您的答案放在文件part1/task5/invalid_user.json然后运行node part1/task5/validate进行检查。

我们的用户架构仍然有什么大问题?

下一步是什么?

到目前为止,您已经知道该标准定义的所有验证关键字,并且您应该能够创建非常复杂的架构。 随着模式的增长,您将重用其中的某些部分。 可以将模式分为多个部分,甚至多个文件,以避免重复。 我们将在本教程的第二部分中进行此操作。

我们还将:

  • 使用架构定义默认值
  • 从数据中过滤其他属性
  • 使用JSON模式规范版本5提案中包含的关键字
  • 定义新的验证关键字
  • 比较现有的JSON模式验证器

谢谢阅读!

翻译自: https://code.tutsplus.com/tutorials/validating-data-with-json-schema-part-1--cms-25343