python中json格式检查_利⽤JSON-Schema对Json数据进⾏校
验(Pyth。。。
本⽂尝试通过json数据校验⽅法解决如下⼏个问题:
数据没有校验,系统处于裸奔状态,导致后期维护成本⾼;
编写⼀堆校验代码,混杂在业务代码中,导致代码可读性降低;
API交付的时候提供⼀⼤段接⼝描述⽂档,但⽤户还是要揣测⽂档意思。
1. 背景介绍
1.1 ⽆处不在的JSON
JSON是⼀种轻量级的数据交换格式,基于Javascript的⼀个⼦集, 但采⽤完全独⽴于语⾔的⽂本格式,易于⼈阅读和编写,同时也易于机器解析和⽣成。这些特性使JSON成为理想的数据交换语⾔, ⼏乎所有与⽹页开发相关的语⾔都有JSON库。⽬前蓝鲸ESB,甚⾄公司内绝⼤多数系统的交互都采⽤JSON格式。
1.2 令⼈头疼的数据校验
由于JSON⽐较灵活,没有固定的schema,使⽤JSON作为数据交换格式时,我们经常遇到数据校验的问题。⼀个简单的JSON数据往往需要写⼀⼤段代码来校验数据格式是否符合预期,导致代码膨胀,可读性不好。
如下是⼀段CC系统新增⾃定义变量的请求参数,⼤致分成⼏部分请求账户、操作者、添加到的⽬标业务和环境类型,最后是要添加的变量列表。
{
"systemId": "", # 系统账号
"password": "", # 系统密码
"operator": "hoffer", # 操作者
"ticket": "", # 操作者的ticket信息
"ApplicationID": 295, # 业务ID
"EnviType": 1, # 环境类型
"Params": [{
"Scope": "[0-1000].gameserver.*.*", # 变量作⽤域
"KeyName": "domain", # 变量名称
"ValName": "awx.zhunter" # 变量值
},
{
"Scope": "[1001-2000].gameserver.*.*", # 变量作⽤域
"KeyName": "domain", # 变量名称
"ValName": "awx.zhunter" # 变量值
setter}]
}
为了校验参数的正确性,往往的做法是写如下⼀段代码(⽤kwargs表⽰请求参数)
# 参数数据类型校验
if not isinstance(kwargs, dict):
return False, "kwargs must be dict"
# 校验systemId是否在参数字段中
if "systemId" not in kwargs:
return False, "systemId is required"
# 校验systemId值的类型
if not instance(kwargs["systemId"], basestring):
return False, "systemId must be string"
# 校验systemId值是否为空
if not kwargs["systemId"]:
return False, "systemId can't be empty"
# 校验Params参数字段
if "Params" not in kwargs:
return False, "Params is required"index什么意思java
if not isinstance(kwargs["Params"], list):
return False, "Params must be list"
for v in kwargs["Params"]:
if not isinstance(v, dict):
return False, "variable must be dict"
.
..
完整的将校验代码写下来需要极⼤的耐⼼,校验代码很简单,但是⼜不太好复⽤,当耐⼼消耗殆尽的时候,我们就开始铤⽽⾛险了,先不去做校验(其实我们都明⽩这有多不好)。
如果不进⾏数据校验,系统相当于裸奔的状态,随时可能出问题,尤其是出现偶发性的数据异常时,往往排查难度⾮常⼤,如果异常发⽣在⼀个逻辑复杂的功能模块中,问题定位花的时间差不多能赶上代码编写的时间了。第三⽅api接⼝格式的变更,如果没能及时通知到调⽤⽅,也会导致潜在的风险。
1.3 ⽤Django-Form校验数据会是什么样呢ckfinder保存路径如何修改
如果⽤django的form做校验,代码会少点,下⾯是⽤django-form对案例数据编写的校验函数:
def validate(kwargs):
class L1Form(forms.Form):
systemId = forms.CharField()
password = forms.CharField()
operator = forms.CharField()
ticket = forms.CharField(required=False)
ApplicationID = forms.IntegerField(min_value=1)
EnviType = forms.ChoiceField(choices=(1, 2, 3))
class VariableForm(forms.Form):
Scope = forms.RegexField(regex="^(\w+|\*|\[[,\d]+\])(\w+|\*|\[[,\d]+\])$")
KeyName = forms.CharField()
python请求并解析json数据ValName = forms.CharField()
l1form = L1Form(data=kwargs)
if not isinstance(kwargs, dict):
return False, "kwargs must be dict"
if not l1form.is_valid():
return False, s
if "Params" not in kwargs:
return False, ""
if not isinstance(kwargs["Params"], list):
return "Params must be list"
for variable in kwargs["Params"]:
if not isinstance(variable, dict):
return False, "variable must be dict"
vf = VariableForm(data=variable)
if not vf.is_valid():
return False, vf.errors
return True, "success"
咋看之下,代码要简单很多了,⽤⼀个django-form可以把⼀层的简单数据类型都校验了,但仔细看看剩下的代码,会发现⼏个问题:
⽤form表单校验⾸先需要保证数据是dict类型
循环结构需要单独编写代码实现
层次关系⽤form校验不了,如果json层次很深,校验代码就退化到了直接编码校验
1.4 当前较为流⾏的RPC框架的解决⽅案——Data Model
回想thrift作为⽬前较为流⾏的⼀个跨语⾔开发框架,使⽤起来就不需要这么繁杂的参数校验,究其原因是因为thrift在接⼝定义的时候严格定义好了接⼝的输⼊输出参数及其类型。 Google的Protocol Buffer也是需要编写⼀个 proto ⽂件,定义程序中需要处理的结构化数据。可见,为了提供可靠的数据,得先有关于数据格式的描述(数据模式),如果对json数据校验的时候,先整理出数据模式,是否也能写个通⽤的检验算法,运⽤模式对数据进⾏校验呢?
2. 模式探索——建⽴数据校验的基⽯
JSON作为javascript的⼀个⼦集,⽀持的数据类型也是可枚举的,基本数据类型有string/number/boolean/null, 容器类型由array和map。容器中容纳的元素是基本数据类型或容器,因此我们只需校验基本数据类型和对容器的结构进⾏校验,容器中的元素可以采⽤递归的⽅式进⾏校验。
由于基本的json数据以key-value的形式存在,可以针对各个字段指定应该满⾜的规则,形式如
"key": {
"rule1_name": rule1_value,
"rule2_name": rule2_value,
...
}
基本数据类型⽐较好校验,可以单独定义⼀些规则⽤于⽀持,⽐如字符串类型,可以定义⼀个规则名stringMaxLength指定最⼤长度, 定义规则名stringFormat指定其格式, 甚⾄定义规则stringPattern指定字符串应该遵循的正则规则。
"key": {
"type": "string",
"stringMaxLength": 100,
"stringMinLength": 1,
"stringFormat": "ipv4"
"stringPattern": ...
}
容易类型的其实也可以按照规则-规则值的⽅式来指定校验规则,容器内的元素⽤递归的⽅式指定校验规则。⽐如⼀个map格式的校验规则就可以写成:
{
"type": "map",
"minKeys": 1,
"maxKeys": 3,
"requiredKeys": ["ApplicationID", "EnviType"]
}
另外还需要有字段专门⽤来指定容器内的元素及其对应的校验规则,可以⽤keys记录
{
"type": "map",
"minKeys": 1,
"maxKeys": 3,
"requiredKeys": ["ApplicationID", "EnviType"],
"keys": {
"key1_name": rule_for_key1,
"key2_name": rule_for_key2,
}
}
3. ⼀个简易的数据校验算法实现
如下代码就是基于上述数据格式定义规则实现的校验算法:
class ValidateError(Exception):
pass
def base_type_validate(data, validate_type):
if validate_type == "string":
if not isinstance(data, basestring):
raise ValidateError("data %s is not type %s" % (data, validate_type)) elif validate_type == "number":
怎么定义结构体if not isinstance(data, (int, float)):
raise ValidateError("data %s is not type %s" % (data, validate_type)) else:
pass # TODO
def stringMaxLength(s, max_length):c语言中strstr函数
# 只对string类型进⾏校验,保证长度不超过max_length
if isinstance(s, basestring):
if len(s) > max_length:
raise ValueError("string length exceed max length %s" % max_length) def validate_data(data, schema):
"""
JSON格式数据校验
:param data:
:param schema:
:return:
"""
for rule, v in schema:
if rule == "type":
base_type_validate(data, v)
elif rule == "stringMaxLength":
stringMaxLength(data, v)
elif rule == "keys":
for _key, _schema in v:
validate_data(_key, _schema)
else:
pass # TODO
4. JSON-Schema
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论