ropi / json-schema-evaluator
JSON Schema Evaluator
v0.4.3
2024-07-22 21:52 UTC
Requires
- php: >=8.1.0
- ext-bcmath: *
- ext-fileinfo: *
- ext-mbstring: *
- guzzlehttp/psr7: ^2.0
- symfony/polyfill-intl-idn: ^1.23
Requires (Dev)
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^9.0
Suggests
- ext-idn: Enables better performance for IDN validations
README
此库是基于PHP实现的用于评估和验证JSON Schemas的库。库可以轻松扩展以使用自己的关键字和草案。
要求
- PHP >= 8.1
- ext-bcmath
- ext-mbstring
- ext-fileinfo
目录
安装
可以通过使用composer从命令行界面安装此库。
支持的草案
草案 2020-12 (核心和验证)
除了以下可选测试之外,通过了官方JSON schema测试套件的所有测试
- optional/refOfUnknownKeyword.json:这意味着您不能使用$ref关键字引用位于未知关键字中的模式。
- optional/ecmascript-regex.json:这意味着不尊重Ecmascript正则表达式的具体细节。相反,正则表达式被评估为PERL正则表达式。
composer require ropi/json-schema-evaluator
基本示例
基本用法
$schema = json_decode('{ "type": "string", "maxLength": 5 }'); $evaluator = new \Ropi\JsonSchemaEvaluator\JsonSchemaEvaluator(); // Each JSON Schema must be statically analyzed once. $staticEvaluationContext = $evaluator->evaluateStatic($schema, new \Ropi\JsonSchemaEvaluator\EvaluationConfig\StaticEvaluationConfig( defaultDraft: new \Ropi\JsonSchemaEvaluator\Draft\Draft202012() )); $instance1 = "hello"; $evaluator->evaluate($instance1, $staticEvaluationContext); // Returns true $instance2 = "helloworld"; $evaluator->evaluate($instance2, $staticEvaluationContext); // Returns false
读取单个错误结果
$valid = $evaluator->evaluate( instance: $instance2, staticEvaluationContext: $staticEvaluationContext, results: $results ); foreach ($results as $result) { /** @var $result \Ropi\JsonSchemaEvaluator\EvaluationContext\RuntimeEvaluationResult */ if ($result->type === 'error') { echo "Error keyword location: '{$result->keywordLocation}'\n"; echo "Error instance location: '{$result->instanceLocation}'\n"; echo "Error message: {$result->error}\n"; } }
上述示例的输出
Error keyword location: '/maxLength'
Error instance location: ''
Error message: At most 5 characters are allowed, but there are 10.
格式化结果
在以下示例中,结果被格式化为基本输出结构。此外,目前也支持标志输出结构。
$formattedResults = (new \Ropi\JsonSchemaEvaluator\Output\BasicOutput($valid, $results))->format(); echo json_encode($formattedResults, JSON_PRETTY_PRINT);
上述示例的输出
{
"valid": false,
"errors": [
{
"type": "annotation",
"valid": true,
"keywordLocation": "\/type",
"instanceLocation": "",
"keywordName": "type",
"error": "",
"errorMeta": null,
"annotation": [
"string"
]
},
{
"type": "error",
"valid": false,
"keywordLocation": "\/maxLength",
"instanceLocation": "",
"keywordName": "maxLength",
"error": "At most 5 characters are allowed, but there are 10.",
"errorMeta": null
}
]
}
变异
默认值
如果使用default关键字定义了默认值,则它可以在评估期间自动应用。
$schema = json_decode('{ "type": "object", "required": ["lastname"], "properties": { "firstname": { "default": "n/a" } } }'); $evaluator = new \Ropi\JsonSchemaEvaluator\JsonSchemaEvaluator(); $staticEvaluationContext = $evaluator->evaluateStatic($schema, new \Ropi\JsonSchemaEvaluator\EvaluationConfig\StaticEvaluationConfig( defaultDraft: new \Ropi\JsonSchemaEvaluator\Draft\Draft202012( evaluateMutations: true ) )); $instance = (object) [ 'lastname' => 'Gauss' ]; $evaluator->evaluate($instance, $staticEvaluationContext); echo $instance->firstname; // Prints "n/a"
内容解码
如果使用contentEncoding关键字定义了编码内容,则可以在评估期间自动解码。
$schema = json_decode('{ "contentMediaType": "application/json", "contentEncoding": "base64" }'); $evaluator = new \Ropi\JsonSchemaEvaluator\JsonSchemaEvaluator(); $staticEvaluationContext = $evaluator->evaluateStatic($schema, new \Ropi\JsonSchemaEvaluator\EvaluationConfig\StaticEvaluationConfig( defaultDraft: new \Ropi\JsonSchemaEvaluator\Draft\Draft202012( evaluateMutations: true ) )); $instance = 'eyJmb28iOiAiYmFyIn0K'; // Base64 encoded JSON '{"foo": "bar"}' $evaluator->evaluate($instance, $staticEvaluationContext); // Returns true echo $instance; // Prints '{"foo": "bar"}'
高级示例
断言内容媒体类型
如果使用contentMediaType关键字定义了内容媒体类型,则可以在评估期间尊重它。
$schema = json_decode('{ "contentMediaType": "application/json" }'); $evaluator = new \Ropi\JsonSchemaEvaluator\JsonSchemaEvaluator(); $staticEvaluationContext = $evaluator->evaluateStatic($schema, new \Ropi\JsonSchemaEvaluator\EvaluationConfig\StaticEvaluationConfig( defaultDraft: new \Ropi\JsonSchemaEvaluator\Draft\Draft202012( assertContentMediaTypeEncoding: true ) )); $instance = '{"foo": "bar"}'; $evaluator->evaluate($instance, $staticEvaluationContext); // Returns true $instance2 = 'invalidJSON'; $evaluator->evaluate($instance2, $staticEvaluationContext); // Returns false
断言格式
如果使用format关键字定义了格式,则可以在评估期间尊重它。
$schema = json_decode('{ "format": "email" }'); $evaluator = new \Ropi\JsonSchemaEvaluator\JsonSchemaEvaluator(); $staticEvaluationContext = $evaluator->evaluateStatic($schema, new \Ropi\JsonSchemaEvaluator\EvaluationConfig\StaticEvaluationConfig( defaultDraft: new \Ropi\JsonSchemaEvaluator\Draft\Draft202012( assertFormat: true ) )); $instance = 'test@example.com'; $evaluator->evaluate($instance, $staticEvaluationContext, $runtimeEvaluationConfig); // Returns true $instance2 = 'invalidEmail'; $evaluator->evaluate($instance2, $staticEvaluationContext, $runtimeEvaluationConfig); // Returns false
短路
默认情况下,即使第一个关键字验证失败,也会评估所有关键字。如果激活短路,则在第一个负验证结果处停止评估。
$config = new \Ropi\JsonSchemaEvaluator\EvaluationConfig\StaticEvaluationConfig( defaultDraft: new \Ropi\JsonSchemaEvaluator\Draft\Draft202012( shortCircuit: true ) );
大数字(将数字字符串解释为数字)
$schema = json_decode('{ "type": "integer" }'); $evaluator = new \Ropi\JsonSchemaEvaluator\JsonSchemaEvaluator(); $staticEvaluationContext = $evaluator->evaluateStatic($schema, new \Ropi\JsonSchemaEvaluator\EvaluationConfig\StaticEvaluationConfig( defaultDraft: new \Ropi\JsonSchemaEvaluator\Draft\Draft202012( acceptNumericStrings: true ) )); $instance = json_decode('6565650699413464649797946464646464649797979', false, 512, JSON_BIGINT_AS_STRING); $evaluator->evaluate($instance, $staticEvaluationContext); // Returns true
自定义关键字
可以将自定义关键字添加到草案中。
以下示例显示了如何实现一个实例必须匹配特定md5散列的关键字。
$schema = json_decode('{ "md5Hash": "098f6bcd4621d373cade4e832627b4f6" }'); class Md5HashKeyword extends \Ropi\JsonSchemaEvaluator\Keyword\AbstractKeyword implements \Ropi\JsonSchemaEvaluator\Keyword\RuntimeKeywordInterface { public function getName() : string { return "md5Hash"; } public function evaluate(mixed $keywordValue, \Ropi\JsonSchemaEvaluator\EvaluationContext\RuntimeEvaluationContext $context): ?\Ropi\JsonSchemaEvaluator\EvaluationContext\RuntimeEvaluationResult { $instance = $context->getCurrentInstance(); if (!is_string($instance)) { // Ignore keyword, because instance is not a string return null; } $result = $context->createResultForKeyword($this, $keywordValue); if (md5($instance) !== $keywordValue) { $result->invalidate('MD5 hash of "' . $instance . '" does not match ' . $keywordValue); } return $result; } } $draft = new \Ropi\JsonSchemaEvaluator\Draft\Draft202012(); $draft->registerKeyword(new Md5HashKeyword(), 'https://example.tld/draft/2022-03/vocab/md5'); // Register keyword with custom vocabulary $evaluator = new \Ropi\JsonSchemaEvaluator\JsonSchemaEvaluator(); $staticEvaluationContext = $evaluator->evaluateStatic($schema, new \Ropi\JsonSchemaEvaluator\EvaluationConfig\StaticEvaluationConfig( defaultDraft: $draft )); $instance = 'test'; $evaluator->evaluate($instance, $staticEvaluationContext); // Returns true, because md5 hash matches $instance = 'hello'; $evaluator->evaluate($instance, $staticEvaluationContext); // Returns false, because md5 hash does not match