如何在JSON Schema验证中实现指定字段的类型转换?
JSON Schema类型转换问题(基于opis/json-schema库)
标准JSON Schema的限制
标准JSON Schema(包括draft-07版本)仅负责验证数据的结构、格式和约束,原生不提供类型转换功能。你提到的default关键字是唯一能修改输入数据的特性,但它仅用于补全缺失的属性值,不涉及类型转换。
基于opis/json-schema库的解决方案
针对PHP环境下的opis/json-schema库,我们可以通过两种方式实现你的类型转换需求:
1. 验证后手动处理类型转换
这是最直接的方案:先通过Schema完成数据验证,再对需要转换的属性单独处理(因为Schema已确保格式合法,转换大概率不会失败)。
示例代码:
use Opis\JsonSchema\Validator; // 你的基础验证Schema $schema = [ "$schema" => "https://json-schema.org/draft-07/schema", "type" => "object", "properties" => [ "some_key" => [ "anyOf" => [ ["type" => "integer"], ["type" => "string", "pattern" => "^(0|[1-9][0-9]*)$"] ] ], // 其他需要转换的属性示例 "another_key" => [ "anyOf" => [ ["type" => "string"], ["type" => "integer"] ] ] ], "required" => ["some_key"], "additionalProperties" => false ]; // 待处理的输入数据 $input = ["some_key" => "123", "another_key" => 456]; // 第一步:验证数据合法性 $validator = new Validator(); $result = $validator->validate($input, $schema); if ($result->isValid()) { // 定义需要转换的属性规则:键为属性名,值为目标类型 $convertRules = [ "some_key" => "integer", // 符合格式的字符串转整数 "another_key" => "string" // 数字转字符串 ]; // 遍历执行转换 foreach ($convertRules as $key => $targetType) { if (!isset($input[$key])) continue; $value = $input[$key]; switch ($targetType) { case "integer": if (is_string($value)) { $input[$key] = (int)$value; } break; case "string": if (is_int($value)) { $input[$key] = (string)$value; } break; } } // $input现在是转换完成的合法数据 var_dump($input); } else { // 处理验证错误 var_dump($result->error()); }
2. 自定义JSON Schema关键字实现自动转换
opis/json-schema支持自定义关键字,你可以创建一个如convertTo的关键字,让验证过程中自动完成类型转换,适合多属性、需要复用转换规则的场景。
示例代码(自定义关键字):
use Opis\JsonSchema\Validator; use Opis\JsonSchema\Schema; use Opis\JsonSchema\ValidationContext; use Opis\JsonSchema\Keyword; // 自定义转换关键字的处理器 class ConvertToKeyword implements Keyword { public function validate(mixed $data, Schema $schema, ValidationContext $context): void { $targetType = $schema->data()->get('convertTo'); if (!$targetType) return; $converted = null; switch ($targetType) { case 'integer': if (is_string($data) && preg_match('/^(0|[1-9][0-9]*)$/', $data)) { $converted = (int)$data; } elseif (is_int($data)) { $converted = $data; // 本身是整数,无需转换 } break; case 'string': if (is_int($data)) { $converted = (string)$data; } elseif (is_string($data)) { $converted = $data; // 本身是字符串,无需转换 } break; } if ($converted !== null) { // 修改上下文数据,完成转换 $context->setData($converted); } else { // 转换失败,抛出验证错误 $context->error('数据类型不符合转换要求', 'convertTo'); } } } // 注册自定义关键字到验证器 $validator = new Validator(); $validator->registerKeyword('convertTo', new ConvertToKeyword()); // 更新Schema,为需要转换的属性添加convertTo关键字 $schema = [ "$schema" => "https://json-schema.org/draft-07/schema", "type" => "object", "properties" => [ "some_key" => [ "convertTo" => "integer", "anyOf" => [ ["type" => "integer"], ["type" => "string", "pattern" => "^(0|[1-9][0-9]*)$"] ] ], "another_key" => [ "convertTo" => "string", "anyOf" => [ ["type" => "string"], ["type" => "integer"] ] ] ], "required" => ["some_key"], "additionalProperties" => false ]; $input = ["some_key" => "123", "another_key" => 456]; $result = $validator->validate($input, $schema); if ($result->isValid()) { // 获取转换后的完整数据 $processedData = $result->data(); var_dump($processedData); } else { var_dump($result->error()); }
总结
- 标准JSON Schema不支持类型转换,必须依赖具体库的扩展或手动处理。
- 手动处理适合简单场景,自定义关键字更适合多属性、需要复用转换规则的场景。
- 两种方式都能实现你需要的“字符串转数字”或“数字转字符串”需求,且转换失败时可通过验证逻辑抛出错误。
内容的提问来源于stack exchange,提问作者afiori
相关产品推荐
相关产品推荐

