我们在实际开发过程中,在开发对外开放接口或者公共模块方法,通常会对入参进行校验,当入参为JSON (Dict)类型数据时,可能需要写几十行代码来校验其数据格式是否符合正确,使得代码冗余度高,可读性差。

本文将介绍一种相对高效简洁且规范的Json 格式入参校验方法 —— JSON Schema,其是基于JSON格式、用于定义JSON数据结构以及数据校验规则,JSON Schema同时还提供anyOf、allOf、oneOf、not、const组合规则,便于我们组合出更加严格的校验规则,满足于复杂JSON入参的校验。

下面,我们将介绍使用 Python jsonschema 模块,借助 JSON Schema的数据结构以及数据校验规则定义,对接口或者公共模块方法入参进行校验,实现数据校验逻辑与业务逻辑的解耦。

安装

pip install jsonschema

JSON Schema 常用关键字细分

我们先通过下面一个简单的JSON Schema示例,初步了解一下 JSON Schema:

from jsonschema import validate, draft7_format_checker
from jsonschema.exceptions import SchemaError, ValidationError# Json schema:
student_schema = {"$schema": "https://json-schema.org/draft/2020-12/schema","title": "student information","description": "some information about test","type": "object","properties": {"id": {"title": "id","description": "Students' id","type": "integer"},        "name": {"title": "name","description": "Students' name","const": "Tony"},"grade": {"description": "Students' scores","type": "number","maximum": 100,},"hobby": {"description": "Students' hobby","type": "array"}},"required": ["name", "grade", "hobby"]
}# json 数据
test_data = {"name": "Tony","grade": 99,"hobby": ["basketball", "football"]
}# 验证
try:validate(instance=json_data, schema=student_schema, format_checker=draft7_format_checker)
except ValidationError as e:print("Json数据验证不通过,异常字段:{} 异常信息:{}".format(" ".join([i for i in e.path]), e.message))
else:print("验证通过")

上述代码示例中,表示对 test_data 依据 student_schema 数据结构以及数据校验规则定义进行验证,其中约束了test_data 必须满足以下要求:

test_data 中必须包含 “name”, “grade”, “hobby” 一级属性/字段(key)。

test_data 中若有id 一级属性/字段(key),则其数据类型必须是整数。

test_data 中若有name 一级属性/字段(key),则其数据值必须等于Tony。

test_data 中若有grade 一级属性/字段(key),则其数据类型必须是整数或浮点数,且最大值不超过100。

test_data 中若有hobby 一级属性/字段(key),则其数据类型必须是列表。

$schema 关键字

$schema 关键字用于指定JSON Schema版本信息,该属性的值必须使用官方提供的版本。

title 与 description 关键字

title 与 description关键字是用来描述对应的待校验 JSON元素的标题和详细描述信息,可以用于最外层对整个JSON对象进行描述,也可以对其中包含的元素进行描述。

type 关键字

该关键字用于限定待校验JSON元素所对应的数据类型,例如,示例中最外层的type关键字值为object,即表示待校验JSON数据为一个JSON对象,而name下的type关键字值为string,即表示待校验JSON对象中的name数据类型为string。

假如实际传入JSON 的name类型不为string,将抛出
jsonschema.exceptions.ValidationError 异常。

type 常见类型对应Python 中的数据类型

下面,我们会结合当type为不同类型,涉及到的关键字用法。

当type 取值为object(JSON)

当type 取值为object(JSON)时,涉及到如下关键字:

properties

required

minProperties、maxProperties

propertyNames

dependencies

patternProperties

additionalProperties

properties 关键字

用于说明待校验的JSON对象中,可能有哪些一级属性/字段(key),以及当对应属性/字段(key)存在时,其value的限制条件。

如下示例,表示当待校验的JSON中存在grade属性/字段时,grade的值必须为number类型,最大值不超过100。

"properties": {"id": {"description": "Students' id","type": "integer",# 设定 minimum 为0 则传入值必须大于等于0"minimum": 0},"grade": {"type": "number",# 设定 maximum 为100, 则传入值必须小于等于 100"maximum": 100,# 设定 maximum 为True ,则传入值必须小于100"exclusiveMinimum": True}}

required 关键字

说明待校验的JSON对象中,必须存在的一级属性/字段(key),该关键字的值是一个数组,数组中的元素必须是字符串,而且必须是唯一的。

如下示例,表示待校验的JSON中的属性/字段(key)必须包含"name", “grade”, “hobby”。

"required": ["name", "grade", "hobby"]

minProperties、maxProperties 关键字

用于限制待校验JSON对象中一级属性/字段(key)的数量限制,minProperties、maxProperties的值都是非负整数。

如下示例,表示限制待校验的JSON对象的一级属性/字段(key)的最大个数为3,最小个数为1。

# 限制一个JSON对象的一级属性/字段(key)的最小个数为1
"minProperties": 1,# 限制一个JSON对象的一级属性/字段(key)的最大个数为3
"maxProperties": 3

propertyNames 关键字

该关键字的值是一个JSON Schema,约束一级属性/字段(key)的名字,需满足正则匹配。

如下示例,表示限制待校验的JSON对象的一级属性/字段(key)的名字需要满足"1" 正则校验。

"propertyNames": {  "pattern": "^[a-z_]"
}

patternProperties 关键字

patternProperties关键字的值是一个JSON对象,该JSON对象的每一个一级key都是一个正则表达式,value都是一个JSON Schema。

只有当待校验JSON对象中的一级属性/字段(key),通过与之匹配的patternProperties中的一级正则表达式,对应的JSON Schema的校验,才算校验通过。

如下示例,待校验JSON对象中,所有以i开头的一级属性/字段(key)的value都必须是integer,所有以g开头的一级属性/字段(key)的value都必须是number。

"patternProperties": {"^i": {"type": "integer"},"^g": {"type": "number"}
}

additionalProperties 关键字

当additionalProperties关键字的值为布尔值时,表示待校验JSON对象中只能出现schema定义的属性/字段,不允许出现额外属性/字段。

{"additionalProperties": False }

当additionalProperties关键字的值为JSON Schema时,表示待校验JSON对象中存在,既没有在properties中被定义,又没有在patternProperties中被定义的属性/字段(key)时,其必须通过additionalProperties的校验。

"additionalProperties":{"type": "array"
}

dependencies 关键字

该关键字的值是一个JSON Schema,表示待校验JSON对象中属性/字段(key)的依赖关系,如下:

"dependencies": {"name": ["id"],
}

可以理解为:

验证通过:不存在id也不存在name

验证不通过:不存在id且存在name

验证通过:存在id且不存在name

完整示例

# Json schema:
student_schema = {"$schema": "https://json-schema.org/draft/2020-12/schema","title": "student information","description": "some information about test","type": "object","properties": {"id": {"description": "Students' id","type": "integer",# 设定 minimum 为0 则传入值必须大于 0"minimum": 0},"name": {"title": "name","description": "Students' name","type": "string"},"grade": {"type": "number",# 设定 maximum 为100, 则传入值必须小于等于100"maximum": 100,# 设定 maximum 为True, 则传入值必须小于100"exclusiveMinimum": True},"hobby": {"description": "Students' hobby","type": "array"}},"required": ["name", "grade", "hobby"],"maxProperties": 10,"patternProperties": {"^id": {"type": "integer"}},"additionalProperties":{"type": "array"},"dependencies": {"name": ["id"],}
}

当type 取值为array(list)

当type 取值为object(array)时,涉及到如下关键字:

items

additionalItems

minItems

maxItems

uniqueItems

contains

items 关键字

该关键字的值是一个有效的JSON Schema或者一组有效的JSON Schema。items用于规定在数组中对应索引位置上应该满足的校验逻辑,只有待校验JSON数组中的所有元素均通过校验,整个数组才算通过校验。

如下示例,表示待校验JSON数组的元素都是string类型,且最小可接受长度是10。

{"type": "array","items": {"type": "string","minLength": 10 }
}

若items 中定义的数量和待校验JSON数组中元素的数量不一致时,则采用 “取小原则” 。即:

若items定义的JSON Schema数量大于待校验JSON数组元素数量,则只校验待校验JSON数组的元素是否符合items中对应索引位置的规则约束。

若items定义的JSON Schema数量小于待校验JSON数组元素数量,则只待校验JSON数据对应元素索引能够在items中JSON Schema中取到值的元素是否符合规则约束。

这种场景比较绕,这里举个例子,items的值如下:

{"type": "array","items": [{"type": "string","minLength": 5},{"type": "number","minimum": 5},{"type": "string"}]
}

上面的JSON Schema指出了待校验JSON数组中对应索引位置元素应该满足的条件,即待校验数组的第一个元素是string类型,且最小长度大于5;数组的第二个元素是number类型,最小值大于5;数组的第三个元素是string 类型。

如下示例,下面这两个JSON数组验证不通过。

["python", 6]
["python", 6, "test", 9527]

如下示例,下面这两个JSON数组验证不通过。

["py", 5]
["python", 3, "test", 9527]

additionalItems 关键字

当additionalItems 关键字的值取值为布尔值时,如为False 时,表示不允许存在额外元素。

{"type": "array","items": [{"type": "string","minLength": 5},{"type": "number","minimum": 5},{"type": "string"}],"additionalItems": False
}

如下示例,该JSON数组验证不通过。

["basketball", 5, "ball", 1]

当additionalItems关键字的值为一组有效的JSON Schema的时候,表示超出items中JSON Schema总数量之外的待校验JSON数组中的剩余的元素应该满足的校验逻辑。

当items的值为一组有效的JOSN Schema的时候,一般和additionalItems关键字组合使用,items用于规定对应位置上应该满足的校验逻辑,而additionalItems用于规定超出items校验范围的所有剩余元素应该满足的条件。

如下示例,表示待校验JSON数组第一个元素是string类型,且可接受的最小长度为5个字符,第二个元素是number类型,且可接受的最小值为5,数组的第三个元素是string类型,剩余的其他元素是number类型,且可接受的最小值为10。

{"type": "array","items": [{"type": "string","minLength": 5},{"type": "number","minimum": 5},{"type": "string"}],"additionalItems": {"type": "number","minimum": 10}
}

如下示例,下面 JSON数组则不能通过校验。

["python", 3, "test", 9527, "schema"]

minItems、maxItems 关键字

minItems、maxItems 关键字的值都是非负整数。指定了待校验JSON数组中元素的属性限制,minItems指定了待校验JSON数组可以接受的最少元素个数,而maxItems指定了待校验JSON数组可以接受的最多元素个数。

如下示例,表示限制一个JSON数组的元素的最大个数不超过10,最小个数不低于2,则JSON Schema如下:

"minItems": 10,
"maxItems": 2

uniqueItems 关键字

uniqueItems 关键字的值是一个布尔值,当该关键字的值为true时,表示待校验JSON数组中的所有元素都具有唯一性时

"uniqueItems": true

完整示例

{"type": "array","items": [{"type": "string","minLength": 5},{"type": "number","minimum": 5},{"type": "string"}],"additionalItems": {"type": "number","minLength": 10},"minItems": 2,"maxItems": 10,"uniqueItems": true
}

当type 取值为integer 或number

当type 取值为integer 或number 时,涉及到如下关键字:

multipleOf

maximum、exclusiveMaximum

minimum、exclusiveMinimum

其中integer 对应Python 中的int 类型,而number 对应Python 中的int 或float 类型。

multipleOf 关键字

multipleOf 关键字的值是一个大于0的number,即可以是大于0的整数,也可以是大于0的浮点数,表示只有待校验的值能够被该关键字的值整除。

如下示例,表示待校验的值需被2整除。

{"type": "integer","multipleOf": 2
}

maximum 关键字

maximum 关键字的值是一个number,即可以是整数,也可以是浮点数。

maximum 关键字规定了待校验元素可以通过校验的最大值,即传入的值必须小于等于maximum 。

如下示例,表示待校验的值需小于等于5。

{"type": "integer","maximum ": 5
}

exclusiveMaximum 关键字

exclusiveMaximum 关键字的值是一个number。exclusiveMaximum 关键字和maximum 一样,规定了待校验元素可以通过校验的最大值,不同的是待校验元素不能等于exclusiveMaximum 指定的值。

如下示例,表示待校验元素需小于100。

{"type": "number",#  设定 exclusiveMaximum为100, 则传入值是小于100"exclusiveMaximum": 100
}

minimum、exclusiveMinimum 关键字

minimum、exclusiveMinimum 关键字的用法和含义与maximum、exclusiveMaximum 相似。唯一的区别在于,一个约束了待校验元素的最小值,一个约束了待校验元素的最大值。

如下示例,表示待校验元素需大于100。

{"type": "number",#  设定 maximum 为10 则传入值必须大于等于10#  "minimum": 10,#  设定 exclusiveMaximum为10,则传入值是大于10"exclusiveMinimum ": 10
}

完整示例

{"type": "number","multipleOf": 2.0,"exclusiveMaximum": 12.5,"exclusiveMinimum": 2.5
}

当type 取值为string
当type 取值为string 时,将涉及到下列关键字:

maxLength

minLength

pattern

format

maxLength 关键字

maxLength 关键字的值是一个非负整数。该关键字规定了待校验JSON元素可以通过校验的最大长度,即待校验JSON元素的最大长度必须小于或者等于该关键字的值。

"maxLength": 5

minLength 关键字

minLength关键字的值是一个非负整数。该关键字规定了待校验JSON元素可以通过校验的最小长度,即待校验JSON元素的最小长度必须大于或者等于该关键字的值。

"minLength": 2

pattern 关键字

pattern关键字的值是一个正则表达式。只有待校验JSON元素符合该关键字指定的正则表达式,才算通过校验。

 "pattern": "^[A-Z]+$"

当type取值为任一类型

当type 取值为任意类型时,都可能涉及到下列关键字::

enum

const

allOf、anyOf、oneOf

not

enum 关键字

enum 关键字的值是一个数组,该数组至少要有一个元素,且数组内的每一个元素都是唯一的。

如下示例,表示待校验的JSON元素与enum关键字数组中的某一个元素相同,则校验通过,enum关键字数组中的元素值可以是任何值,包括null。例如:

{"type": "number","enum": [2, 3, null, "hello"]
}

const 关键字

const 关键字的值可以是任何值,包括null。如果待校验的JSON元素的值与const关键字指定的值相同,则通过校验。

如下示例,表示只有待校验JSON元素等于const关键字的值 Jon 时,则校验通过。

{"const": "Jon"}

allOf 关键字

allOf 关键字的值是一个非空数组,数组里面的每个元素都必须是一个有效的JSON Schema。

如下示例,表示只有待校验JSON元素通过anyOf关键字数组中所有的JSON Schema校验,则校验通过。

{"anyOf": [{ "type": "string", "maxLength": 5 },{ "type": "number", "minimum": 0 }]
}

anyOf 关键字

anyOf 关键字的值是一个非空数组,数组里面的每个元素都必须是一个有效的JSON Schema。

如下示例,表示待校验JSON元素能够通过anyOf关键字数组中的任何一个JSON Schema校验,则校验通过。

{"allOf": [{ "type": "string" },{ "maxLength": 5 }]
}

oneOf 关键字

oneOf 关键字的值是一个非空数组,数组里面的每个元素都必须是一个有效的JSON Schema。

如下示例,表示待校验JSON元素能且只能通过oneOf关键字数组中的某一个JSON Schema校验,则校验通过。若不通过任何一个校验或通过两个及以上的校验,则校验不通过。

{"type": "number","oneOf": [{ "multipleOf": 5},{ "multipleOf": 3}]
}

not 关键字

not 关键字的值是一个 JSON Schema。

如下示例,表示只有待校验JSON元素不能通过not 关键字指定的JSON Schema校验的时候,则校验通过。

{"not": {"type": "string"}
}

type 关键字为一个数组

这里需要特别注意的是,type 关键字的值除了可以是string,也可以是一个数组。

如果type 关键字的值是一个数组,则数组中的元素都必须是string,且其取值被限定为null、boolean、object、array、number、string、integer,只要带校验JSON元素是其中的一种,则校验通过。

JSON Schema 常用关键字汇总

from jsonschema import validate, draft7_format_checker
from jsonschema.exceptions import SchemaError, ValidationErrorstudent_schema = {"$schema": "https://json-schema.org/draft/2020-12/schema","title": "student information","description": "some information about test","type": "object","properties": {"id": {"title": "id","description": "Students' id","type": "integer","minimum": 1},"name": {"title": "name","description": "Students' name","minLength": 3,"maxLength": 30},"school":{"title": "school","description": "Students' school","const": "Hexi Foreign Language Senior High School"},"grade": {"description": "Students' scores","type": "number","minimum":0,"maximum":100,"multipleOf": 0.5,},"phone": {"title": "phone","description": "Students' phone","type": "string","pattern": "^1[3|4|5|7|8|9][0-9]{9}$"},"hobby": {"description": "Students' hobby","type": "array","items": [{"type": "string","minLength": 5},{"type": "number","minimum": 5}],"additionalItems":{"type": "string","miniLength": 2},},"remark": {"description": "Students' remark","type": "object","properties": {"father_name": {"type": "string"},"mother_name": {"type": "string"}}}},"required": ["name", "grade"],"minProperties": 7,"maxProperties": 10,"patternProperties": {"^id": {"type": "integer"}},"dependencies": {"name": ["id"],},"additionalProperties":{"type": "array"}
}json_data = {"id": 1,"name": "Tom","school": "Hexi Foreign Language Senior High School","grade": 96.5,"phone": "19825001234","remark": {"father_name":"Tony", "mother_name":"Mary"},"subject": ["math", "chinese"]
}try:validate(instance=json_data, schema=student_schema, format_checker=draft7_format_checker)
except ValidationError as e:print("Json数据验证不通过,异常字段:{} 异常信息:{}".format(" ".join([i for i in e.path]), e.message))
else:print("验证通过")

下面是测试资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!

最后: 可以在公众号:伤心的辣条 ! 免费领取一份216页软件测试工程师面试宝典文档资料。以及相对应的视频学习教程免费分享!,其中包括了有基础知识、Linux必备、Shell、互联网程序原理、Mysql数据库、抓包工具专题、接口测试工具、测试进阶-Python编程、Web自动化测试、APP自动化测试、接口自动化测试、测试高级持续集成、测试架构开发测试框架、性能测试、安全测试等。

学习不要孤军奋战,最好是能抱团取暖,相互成就一起成长,群众效应的效果是非常强大的,大家一起学习,一起打卡,会更有学习动力,也更能坚持下去。你可以加入我们的测试技术交流扣扣群:914172719(里面有各种软件测试资源和技术讨论)

喜欢软件测试的小伙伴们,如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一键三连哦!


好文推荐

转行面试,跳槽面试,软件测试人员都必须知道的这几种面试技巧!

面试经:一线城市搬砖!又面软件测试岗,5000就知足了…

面试官:工作三年,还来面初级测试?恐怕你的软件测试工程师的头衔要加双引号…

什么样的人适合从事软件测试工作?

那个准点下班的人,比我先升职了…

测试岗反复跳槽,跳着跳着就跳没了…


  1. a-z_ ↩︎

Python 使用Schema 实现接口或方法的JSON格式参数的高效、优雅校验相关推荐

  1. C# WebAPI中DateTime类型字段在使用微软自带的方法转json格式后默认含T的解决办法...

    C# WebAPI中DateTime类型字段在使用微软自带的方法转json格式后默认含T的解决办法 原文:C# WebAPI中DateTime类型字段在使用微软自带的方法转json格式后默认含T的解决 ...

  2. Python 数据可视化—下载数据(CSV文件格式、JSON格式)

    Python 数据可视化-下载数据CSV文件格式.JSON格式 网上下载数据,并对这些数据进行可视化,可视化以两种常见格式存储的数据:CSV 和JSON. 我们将使用Python模块csv 来处理以C ...

  3. php api json,PHP API接口必备之输出json格式数据实例详解

    这篇文章主要给大家介绍了关于PHP API接口必备之输出json格式数据的相关资料文中通过示例代码介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面来一起看看吧. 前言 我们在日常的开发工 ...

  4. 抽象类、接口作为方法返回值和参数

    1.抽象类作为方法的参数 抽象类作为方法参数传递时,需要传入一个实现抽象类所有抽象方法的子类对象. 2.抽象类作为方法的返回值 抽象类作为方法的返回值时,需要返回一个实现了抽象类所有抽象方法的子类对象 ...

  5. python中drop用法_Python drop方法删除列之inplace参数实例

    drop方法有一个可选参数inplace,表明可对原数组作出修改并返回一个新数组.不管参数默认为False还是设置为True,原数组的内存值是不会改变的,区别在于原数组的内容是否直接被修改.默认为Fa ...

  6. 【学习笔记】MyBatis中接口中方法传递多个参数问题展示与解决

    问题展示 编写Mapper接口 public interface ProductsDao {public Products findById(int i);public void deleteById ...

  7. python解析response_python:解析requests返回的response(json格式)说明

    我就废话不多说了,大家还是直接看代码吧! import requests, json r = requests.get("http://192.168.207.160:9000/api/qu ...

  8. Java实现向指定URL用POST方法发送Json格式字符串参数请求的工具类

    场景 SpringBoot项目中通过后台Controller向某服务接口发送POST请求. 实现 在项目pom.xml中添加依赖 <dependency><groupId>co ...

  9. python遍历json_python json格式参数遍历所有key、value 及替换key对于的value

    1.对于接口自动化测试,一般接口以json形式发送返回,往往我们就需要遍历json文件中所有key,value以及修改替换key对于的value. 例如json发送/接收的文件: SendRegist ...

最新文章

  1. 自学了python基础英语_Python自学路线图之Python基础自学
  2. 面对复杂业务,if-else coder 如何升级?
  3. python散点图图例只显示一个标记点_python – Matplotlib图例:如何分配多个散点值...
  4. KAFKA 集成 SpringBoot2 消息发送和消费消息(基础篇)
  5. kitti数据集反代下载
  6. for vue 一行2列_前端开发面试问什么?vue面试中经常问到的问题?用vue想拿20k,面试题要这样答!...
  7. 虹软人脸识别在 linux中so文件加载不到的问题
  8. JavaScript——创建运动框架
  9. linux上搭载was应用上传中文文件,受支持的Linux操作系统和WAS ND 9.0安装部署文档的资料说明...
  10. 计算机是如何启动的?(转载)
  11. 电脑开机出现英文“ERROR:System fan has failed”的错误提示
  12. NYOJ477 - A+B Problem III
  13. 艰难前行的故事 (《梦断代码》读后感)
  14. cenos 安装 Docker
  15. 我的家乡主题网页设计
  16. 天线年会 闭幕式 ppt_OracleVS谷歌–闭幕式,陪审团审议版权阶段
  17. FFS(快速文件系统)–Unix文件系统
  18. Compuware 公司
  19. 大快搜索黑科技亮相2019(第四届)大数据产业生态大会,斩获多项大奖
  20. vue 项目启动失败 ‘webpack-dev-server‘ 不是内部或外部命令,也不是可运行的程序

热门文章

  1. python积分计算_fx-CG系列图形计算器CAS应用程序Khicasen
  2. c语言dfs算法,DFS算法源程序
  3. 记 计算机 科学学院 教师,学风浓厚,桃李芬芳—记计算机学院金国祥老师
  4. Python+OpenCV:基于KNN手写数据OCR(OCR of Hand-written Data using kNN)
  5. 连接MySQL和连接文件夹区别_JDBC与JNDI这两种连接方式有什么区别?
  6. 项目工程自动化 - OCLint
  7. 凭什么相信你,我的CNN模型
  8. Linux学习总结(六十六)打印一串数字的脚本
  9. vim编辑器操作汇总
  10. iOS静态库.a文件制作和导入使用