JSON文件的校验——保证正确的数据格式


前提

在上一篇《JSON文件的应用》中我说过,从客户端上传到服务器上的JSON字符串是需要校验的。这篇博文的主要内容包含JSON字符串为什么需要校验,以及如何校验等。


为什么需要校验

大家都知道,服务器是非常宝贵的公共资源。因此,对于服务器上的文件应该具有更加严格的要求。针对JSON文件,主要有以下两个方面:

1.保证数据的安全

主要举两个例子:
①如果因为客户端程序的问题,向服务器传回了一份有缺陷的JSON字符串(比如括号不匹配,丢失逗号分隔符等)。

②如果同样是因为客户端程序的问题,向服务器传回的JSON字符串中,用户联系人里面有一个人居然没有ID和姓名。

总之,客户端是不可信的,如果服务器程序直接将上述JSON字符串覆盖回用户对应的JSON文件里,那么这个用户可能每次打开以这个账户登录APP都会闪退,甚至只能重新申请一个新的账号。

2.维护服务器的安全

试想,如果一个恶意攻击者将一段包含Shell命令的字符串——而不是正确的JSON字符串——传到了服务器。那么可能他只需用这个账户登录APP就可以获得服务器的控制权,这简直是灾难!

因此,无论出于哪方面考虑,我们都必须对从客户端传到服务器的JSON字符串进行校验。


JSON Schema

JSON Schema用来描述JSON数据格式。它有多种用途,其中之一就是实例验证。验证过程可以是交互式或非交互式的。例如,应用程序可以使用JSON模式来构建用户界面使互动的内容生成除了用户输入检查或验证各种来源获取的数据。[1]

网上的概念比较晦涩难懂。按照我的理解,JSON Schema类似于抽象语法树。至于它长什么样,后面我会给出一个具体的例子。


由JSON字符串生成对应的JSON Schema

一段JSON字符串样例

下面这段JSON字符串在《JSON文件的应用》中贴过,为了便于说明,再贴一次。

{
  "contacters": [
    {
      "id": "10101",
      "name": "张三",
      "age": 22,
      "singleOrNot": true
    },
    {
      "id": "10102",
      "name": "李四",
      "age": 24
    }
  ]
}

对应的JSON Schema

{
  "definitions": {},
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "http://example.com/root.json",
  "type": "object",
  "title": "The Root Schema",
  "required": [
    "contacters"
  ],
  "properties": {
    "contacters": {
      "$id": "#/properties/contacters",
      "type": "array",
      "title": "The Contacters Schema",
      "items": {
        "$id": "#/properties/contacters/items",
        "type": "object",
        "title": "The Items Schema",
        "required": [
          "id",
          "name",
          "age",  //注①
          "singleOrNot"
        ],
        "properties": {
          "id": {
            "$id": "#/properties/contacters/items/properties/id",
            "type": "string",
            "title": "The Id Schema",
            "default": "",
            "examples": [
              "10101"
            ],
            "pattern": "^(.*)$"
          },
          "name": {
            "$id": "#/properties/contacters/items/properties/name",
            "type": "string",
            "title": "The Name Schema",
            "default": "",
            "examples": [
              "张三"
            ],
            "pattern": "^(.*)$"
          },
          "age": {
            "$id": "#/properties/contacters/items/properties/age",
            "type": "integer",
            "title": "The Age Schema",
            "default": 0,
            "examples": [
              22
            ]
          },
          "singleOrNot": {
            "$id": "#/properties/contacters/items/properties/singleOrNot",
            "type": "boolean",
            "title": "The Singleornot Schema",
            "default": false,
            "examples": [
              true
            ]
          }
        }
      }
    }
  }
}

注释:
①age和singleOrNot应该删掉,因为它们和id、name不同,不是必须的。被"required"约束的属性,每个成员必须具有。但是在我们的例子中,有些联系人我们是不需要记录他的年龄和是否单身的,因此这两个属性不是必须的。否则会因此发生校验失败。


生成JSON Schema

这里给大家介绍一个网站——JSON Schema Tool,我们可以用它来生成自己的JSON Schema。
在这里插入图片描述
我们只需要在左边的Your JSON*输入框输入自己的JSON字符串(需要语法正确),然后点击INFER SCHEMA按钮,即可看到右边生成的JSON Schema。

需要注意的是,右边一共三个选项卡,我们复制中间的那个里面的Schema字符串就可以了,第一个是一种树形结构,第三个复制出来会带有行号。


服务端校验

在服务端校验JSON文件需要执行以下的步骤:

1.保存Schema字符串到文件

保存刚刚生成的Schema字符串到一个文件(如UserSchema.json),所有用户记录的校验都依赖于该文件。

2.引入Maven依赖

要校验JSON需要先引入json-schema-validator的依赖。在pom.xml文件的<dependencies>标签中添加以下代码:

        <!-- https://mvnrepository.com/artifact/com.github.fge/json-schema-validator -->
        <dependency>
            <groupId>com.github.fge</groupId>
            <artifactId>json-schema-validator</artifactId>
            <version>2.2.6</version>
        </dependency>

3.校验的代码

    public ProcessingReport validate(String dataString, String schemaString) {
        ProcessingReport processingReport = null;
        try {
            JsonNode dataNode = JsonLoader.fromString(dataString);
            JsonNode schemaNode = JsonLoader.fromString(schemaString);
            JsonSchema schema = JsonSchemaFactory.byDefault().getJsonSchema(schemaNode);
            processingReport = schema.validate(dataNode);
            return processingReport;
        } catch (IOException | ProcessingException ex) {
            Logger.getLogger(JsonService.class.getName()).log(Level.SEVERE, null, ex);
        }
        return processingReport;
    }

函数会返回一个ProcessingReport类型的对象,调用该对象的isSuccess函数返回的boolean值即表示校验成功或失败。然后就可以根据这个结果来决定要不要用这份客户端上传的新用户记录来覆盖老的用户记录了。


后记

JSON的校验其实可以相当复杂,可以看到JSON Schema Tool为我们生成的JSON Schema框架里面有一个属性叫"pattern":

    "pattern": "^(.*)$"

很明显它是一个无意义的正则表达式,如果我们需要更加严格的校验规则(比如年龄限定在0-120以内),就需要修改这个正则表达式或者其他的属性。而这部分的知识,应该又是一本小册子的事情了,学到了再更新。


  1. 摘自百度百科——jsonschema ↩︎

  • 5
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IMplementist

你的鼓励,是我继续写文章的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值