Opis JSON Schema验证PHP数据提示需匹配object类型报错排查
Opis JSON Schema校验关联数组报类型不匹配问题
问题复现
在PHP项目中使用Opis JSON Schema实现JSON数据校验时,会遇到传入符合字段规则的关联数组却被提示类型不匹配的问题,复现条件如下:
- 编写的Schema文件
user-data.schema.json内容如下:
{ "$id": "https://www.example.com/user-data.schema.json", "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "User Data", "type": "object", "required": [ "regIpAddress" ], "properties": { "regIpAddress": { "type": "string" } }, "additionalProperties": true }
- 编写的校验逻辑代码如下:
$validator = new Validator(); $resolver = $validator->loader()->resolver(); $resolver->registerPrefix('https://www.example.com/', '/app/web/modules/custom/mymodule/schema'); $result = $validator->validate($json_array, 'https://www.example.com/user-data.schema.json'); if ($result->isValid()) { $encoded_preferences = json_encode($json_array); $this->set('myfield', json_encode($json_array)); } else { $error = $result->error(); $formatter = new ErrorFormatter(); // 打印辅助方法 $print = function ($value) { return json_encode( $value, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ); }; $user_id = $this->id(); $error_message = "User id: $user_id " . $print($formatter->formatOutput($error, "verbose")); $encoded_preferences = json_encode($json_array); throw new InvalidJsonException("JSON user data failed to validate! $error_message, $encoded_preferences");
- 传入的待校验数据为PHP关联数组,内容如下:
[ "regIpAddress" => "host.docker.internal", "emailVerifyCode" => "965", ]
- 校验时返回如下错误:
{ "keywordLocation": "#/type", "instanceLocation": "#", "error": "The data ({type}) must match the type: object", "valid": false, "absoluteKeywordLocation": "https://www.example.com/user-data.schema.json#/type" }
- 临时处理方式:校验前对
$json_array做一次json_encode再json_decode处理即可通过校验,代码如下:
$json_array = json_decode(json_encode($json_array)); $result = $validator->validate($json_array, 'https://www.example.com/user-data.schema.json');
问题根因
该问题的核心原因是Opis JSON Schema的类型校验严格对齐JSON类型规范:
*JSON标准中的object类型,在校验逻辑中仅匹配PHP的stdClass实例;PHP的关联数组本质为array类型,会被默认识别为JSON array类型,和Schema中声明的type: object规则不匹配,因此校验器直接抛出顶层类型错误,不会进入后续字段规则校验流程。
临时方案中json_encode拼接json_decode之所以能生效,是因为默认配置下json_decode不传第二个参数时,会将JSON对象解析为stdClass实例,类型符合校验要求后即可正常执行后续校验。
正确修复方案
json_encode+json_decode的处理方式存在不必要的序列化/反序列化开销,数据量较大时性能损耗明显,不推荐使用。正确处理方式非常简单,传入校验数据前将关联数组强转为stdClass对象即可,转换效率远高于编解码方案:
// 将PHP关联数组强转为stdClass对象,对齐JSON object类型要求 $validateData = (object)$json_array; $result = $validator->validate($validateData, 'https://www.example.com/user-data.schema.json');
如果业务逻辑里必须保留数组格式处理数据,也可以仅在校验环节做一次对象转换,其余流程继续使用关联数组即可,不需要改动原有业务逻辑。
内容的提问来源于stack exchange,提问作者Patrick Kenny
相关产品推荐
相关产品推荐

