如果你是一名开发人员,那么你很可能经常使用JSON。但是,如何定义和验证JSON数据,以及如何避免因JSON数据格式错误而引发的问题呢?

本文将探讨什么是JSON模式,介绍一些JSON模式的示例,并说明谁来决定JSON模式的具体结构应该是什么样的。

先决条件

要理解这篇文章的内容,你需要了解一些JSON的基础知识,同时还需要对API的工作原理有基本的了解

目录

JSON究竟是什么?

JSON是一种常用的文档格式,通常用于通过API进行数据交换。它体积小、易于人类和机器阅读,而且许多编程语言都支持它。

但是,JSON对象并不具有固定的结构,可以用来表示不同类型的数据。因此,虽然JSON非常灵活,但它并不会强制规定数据必须遵循某种特定的结构或类型。这种灵活性也可能导致在数据交换过程中出现问题。

例如,如果服务器期望接收购物车中的商品信息,但却接收到包含支付交易数据的对象,那么服务器可能会无法正确解析这些数据,甚至可能因此崩溃。

另外,如果一个应用程序的服务器负责处理用户之间的消息传递,并且期望接收具有特定结构的数据,但客户端发送了格式不同的数据,那么在解析过程中就可能会出现问题,甚至可能导致安全漏洞。

正因为如此,才需要进行数据验证。

什么是验证?

在任何数据被发送之前对其进行验证,始终是一种良好的做法。验证就是将用户输入的数据与预期的结构及数据类型进行对比检查。

如果输入的数据格式、结构或数据类型与预期不符,那么该表单就会被拒绝,不会被发送到服务器。这样就可以避免服务器尝试解析错误类型的数据,从而防止潜在的安全问题发生。

JSON模式是一种用于描述其他JSON数据应具备的格式结构的文档。它实际上限制了服务器能够接收和处理的JSON数据的类型,确保只有正确类型的数据才能被服务器成功解析。

在Node.js中,像`package.json`这样的配置文件,甚至Azure的ARM模板,都是使用JSON格式编写的。这些文件必须经过验证,以确保其语法和结构是正确的,这样系统才能理解它们。验证工具的作用就是进行这种检查,从而帮助避免因解析错误而引发的各种问题。

JSON模式的结构

普通的JSON文件包含数据,而JSON模式则包含了关于这些数据的规则。这种模式是一种声明性的格式。

让我们来看一个包含数据的普通JSON文件的例子:

 {
  "username": "chidiadi",
  "followers": 2200
}

现在,这个JSON模式定义了该数据的结构及约束规则:

{
  "type": "object",
  "properties": {
    "username": {
      "type": "string"
    },
    "followers": {
      "type": "integer"
    }
  }
}

JSON模式本身也是由JSON对象构成的,但这些对象实际上代表了数据的约束条件及语法规范。

例如,这个模式要求有一个包含两个属性的JSON对象:`username`和`followers`。其中`username`属性必须是字符串类型(这是JSON支持的数据类型),而`followers`属性则必须是整数类型。整个父对象属于“object”类型。

这就是JSON模式的基本结构。根据实际需求,你可以在此基础上构建更复杂、更全面的模式。

如何创建JSON模式

要创建一个基本的JSON模式,你需要完成以下步骤:

  1. 使用`$schema`关键字来定义模式。

  2. 使用`$id`关键字为该模式指定一个唯一的标识符(URI)。

  3. 然后添加`title`和`description`关键字,这些是模式的注释信息。

  4. 最后定义`type`和`properties`等关键字,以规定数据的验证规则。

`$schema`关键字用于说明你的模式遵循的是哪个版本的JSON规范。例如:

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

`$id`关键字用于为模式指定一个唯一的URI,这样就可以在其他模式中引用它了。这种方式有助于重复使用和整理各种模式。例如:

 { 
    "$id": "http://yourdomain.com/schemas/followers.json" 
}

之后,就可以在另一个模式中使用`$ref`关键字来引用这个模式了。

 { 
    "$ref": "http://yourdomain.com/schemas/followers.json" 
}

`title`和`description`关键字属于注释信息,它们用于说明该模式的用途。例如:

 { 
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "http://yourdomain.com/schemas/followers.json"   
  "title": "粉丝数量模式", 
  "description": "用于描述每个人的粉丝数量"
}

到目前为止,我们提到的所有关键字都不会影响验证的结果。这些关键字并没有说明预期的JSON文档应该是什么样的,也没有规定应该依据什么来进行验证。而`type`和`properties`这两个关键字恰恰起到了这样的作用——它们就是用于进行验证的关键字。

`type`关键字用于指定正在被该模式验证的对象的类型。这种类型可以是`string`、`object`、`array`、`number`、`boolean`或`null`。

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "http://yourdomain.com/schemas/followers.json",
  "title": "Followers Schema",
  "description": "Number of followers per person",
  "type": "object"
}

如果你发送的JSON文件中包含一个作为父元素的数组,那么这个文件将会被拒绝。但是,这个对象应该包含哪些内容呢?这就是`properties`关键字发挥作用的地方了。

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "http://yourdomain.com/schemas/followers.json",
  "title": "Followers Schema",
  "description": "Number of followers per person",
  "type": "object",
  "properties": {
    "username": {
      "description": "The username of the user",
      "type": "string"
    },
    "followers": {
      "description": "The number of followers the user has",
      "type": "integer"
    }
  }
}

在这里,我们添加了JSON文件中应该包含的键:`username`和`followers`。因此,那些包含其他键的文件将会无法通过验证。

以下这个JSON文件是符合要求的:

{
  "username": "chidiadi",
  "followers": 2200
}

而以下这个JSON文件则不符合要求:

{
  "username": "chidiadi",
  "name": "Chidiadi Anyanwu",
  "followers": 2200,
}

它之所以会失败,是因为JSON文件中多出的`name`键并没有被包含在模式定义中。那些键中的`description`字段仅仅是一些注释,用于向开发者或查看模式文件的人说明这些键的含义而已。

还有一个`required`关键字,可以用来指定JSON文件中哪些键是必须存在的,否则文件就无法通过验证。例如,如果我们希望拒绝任何没有填写`username`字段的JSON文件,就可以将这个字段设置为必填项。

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "http://yourdomain.com/schemas/followers.json",
  "title": "Followers Schema",
  "description": "Number of followers per person",
  "type": "object",
  "properties": {
    "username": {
      "description": "The username of the user",
      "type": "string"
    },
    "followers": {
      "description": "The number of followers the user has",
      "type": "integer"
    }
  },
  "required": ["username"]
}

required关键字是一种用于对象验证的关键字,其值必须是一个数组。如果不使用这个关键字,那么该数组就相当于一个空数组。你可以在这个数组中添加多个键。例如,如果我们希望JSON文件中的用户名和粉丝数量都是必填字段,那么就可以将这两个字段都包含在数组中:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "http://yourdomain.com/schemas/followers.json",
  "title": "粉丝信息规范",
  "description": "每个人拥有的粉丝数量",
  "type":"object",
  "properties":{
    "username": {
      "description": "用户的用户名",
      "type": "string"
    },
    "followers": {
      "description": "用户拥有的粉丝数量",
      "type": "integer"
    }
  },
  "required":["username","followers"]
}

JSON规范的应用场景

OpenAPI

根据OpenAPI倡议的定义,

“OpenAPI规范为HTTP接口定义了一种与编程语言无关的标准接口描述方式,这种方式能够让人类和计算机在不需要访问源代码、额外文档或检查网络流量的情况下,了解某个服务的功能。”

简而言之,OpenAPI为开发人员提供了一种能够清晰地记录他们的API设计的方式,这样他们就可以方便地跟踪自己的开发进度。同时,对于那些需要使用这些API的人来说,这种规范也能帮助他们理解如何正确使用这些接口。

OAS是一种用于描述API的规范。它利用基于JSON格式的规范定义来说明请求和响应的结构。OpenAPI文档也可以用JSON或YAML格式编写。

{
  "openapi": "3.2.0",
  "info": {
    "title": "计数器API",
    "version": "1.0.0",
    "description": "一个用于存储和增加计数值的简单API"
  },
  "servers": [
    {
      "url": "https://api.example.com"
    }
  ],
  "paths": {
    "/counter": {
      "get": {
        "summary": "获取当前的计数器值",
        "operationId": "getCounter",
        "responses": {
          "200": {
            "description": "当前计数器值",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Counter"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "增加计数器的数值",
        "operationId": "incrementCounter",
        "responses": {
          "200": {
            "description": "更新后的计数器值",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Counter"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Counter": {
        "type": "object",
        "properties": {
          "value": {
            "type": "integer",
            "example": 42
          }
        },
        "required": ["value"]
      }
    }
  }
}

对于 YAML 格式来说:

openapi: 3.2.0

info:
  title: 计数器 API
  version: 1.0.0
  description: 这是一个用于存储和增加计数值的简单 API。

servers:
  - url: https://api.example.com

paths:
  /counter:
    get:
      summary: 获取当前的计数器值
      operationId: getCounter
      responses:
        "200":
          description: 当前的计数器值
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Counter"

    post:
      summary: 增加计数器的数值
      operationId: incrementCounter
      responses:
        "200":
          description: 更新后的计数器值
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Counter"

components:
  schemas:
    Counter:
      type: object
      properties:
        value:
          type: integer
          example: 42
      required:
        - value

在这里,我们定义了一个名为 “Counter” 的模式对象。这个模式对象描述了 API 应该接收和发送的请求数据以及响应数据的结构。

该 API 支持两种 HTTP 方法:使用 POST 方法增加计数器的数值,使用 GET 方法获取当前的计数器值。其 URL 为 “https://api.example.com/counter”。

Azure 资源管理器模板

Azure 资源管理器负责在 Azure 账户中创建、删除和更新资源。Azure 门户、REST 客户端、Azure PowerShell 以及 Azure CLI 都依赖它来配置资源。您也可以使用 ARM 模板来定义基础设施的配置方案。

ARM 模板是一种 JSON 文件,它以声明性的方式描述了一个项目的基础设施配置信息。该模板指定了需要部署到特定租户、资源组、订阅或管理组中的资源类型。


{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "location": {
      "type": "string",
      "defaultValue": "[resourceGroup().location]"
    }
  },
  "resources": [
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2025-06-01",
      "name": "mystorageaccount",
      "location": "[parameters('location')]",
      "sku": {
        "name": "Standard_LRS"
      },
      "kind": "StorageV2"
    }
  ]
}

这个模板会创建一个名为 mystorageaccount 的存储账户,其位置取自相应的资源组。

如您所见,该模板中包含了一个 “schema” 关键字,这个关键字链接到了 Azure 团队制定的 JSON 模式文件,用于验证 ARM 模板的内容。因此,在部署模板之前,系统会先根据该模式文件对模板进行验证。

JSON模式项目

人们很容易将“JSON模式”这一概念与“JSON模式项目”混为一谈。但实际上,JSON模式是一个开源项目,它与互联网工程任务组(IETF)密切合作,其目标是统一构建JSON模式的规范。

关键在于,世界各地的所有开发人员在创建JSON模式时都可以使用相同的模板、格式和结构,这样一来,使用JSON的各类系统之间的互操作性就会得到提升。而这正是JSON作为一种文件交换格式所追求的目标。

如下图所示,Azure ARM模板模式就是基于JSON模式项目中某个版本的规范构建而成的:

49cbf0f6-7dbe-4dfb-bf2b-f8d6204a44c4

如果您想了解更多关于JSON模式项目的信息,可以访问他们的官方网站

结论

JSON模式在世界各地都被广泛使用,但人们对它的了解往往不够深入,也不常讨论它。实际上,模式是一种基础性技术,许多API以及OpenAPI、AsyncAPI、HL7 FHIR和SDFormat等项目都依赖于这种技术,因此作为开发者,了解JSON模式是非常有必要的。如果您喜欢这篇文章,请分享给更多人。您也可以通过LinkedInX与我联系。

Comments are closed.