opis / json-schema

JSON Schema validator for PHP
https://opis.io/json-schema
Apache License 2.0
566 stars 57 forks source link

BUG: Keyword oneOf doesn't contain sub errors in version 2.x #98

Closed liborm85 closed 2 years ago

liborm85 commented 2 years ago

Validation in version 1.x return sub errors for keyword oneOf, but version 2.x return only The data should match exactly one schema. Keyword anyOf works correctly with sub errors in both versions. I think both keywords oneOf and anyOf should work similarly.

See examples bellow.

Details

Examples for reproduce:

Example to reproduce issue in version 1.1.0 ```php use Opis\JsonSchema\{ Validator, ValidationResult, ValidationError, Schema }; $dataJson = <<<'JSON' { "case1A": "foo" } JSON; $schemaJson = <<<'JSON' { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "oneOf": [ { "$comment": "Case 1", "additionalProperties": false, "properties": { "case1A": { "type": "string" }, "case1B": { "type": "string" } }, "required": [ "case1A", "case1B" ] }, { "$comment": "Case 2", "additionalProperties": false, "properties": { "case2A": { "type": "string" }, "case2B": { "type": "string" } }, "required": [ "case2A", "case2B" ] }, { "$comment": "Case 3", "additionalProperties": false, "properties": { "case3A": { "type": "string" }, "case3B": { "type": "string" } }, "required": [ "case3A", "case3B" ] } ] } JSON; $data = json_decode($dataJson); $schema = Schema::fromJsonString($schemaJson); $validator = new Validator(); /** @var ValidationResult $result */ $result = $validator->schemaValidation($data, $schema); if ($result->isValid()) { echo '$data is valid', PHP_EOL; } else { /** @var ValidationError $error */ $error = $result->getFirstError(); echo '$data is invalid', PHP_EOL; echo "Error: ", $error->keyword(), PHP_EOL; echo json_encode($error->keywordArgs(), JSON_PRETTY_PRINT), PHP_EOL; foreach ($error->subErrors() as $subError) { echo "Sub Error: ", $subError->keyword(), PHP_EOL; echo json_encode($subError->keywordArgs(), JSON_PRETTY_PRINT), PHP_EOL; } } ```
Example to reproduce issue in version 2.2.0 ```php use Opis\JsonSchema\{ Validator, ValidationResult, Errors\ErrorFormatter, }; $dataJson = <<<'JSON' { "case1A": "foo" } JSON; $schemaJson = <<<'JSON' { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "oneOf": [ { "$comment": "Case 1", "additionalProperties": false, "properties": { "case1A": { "type": "string" }, "case1B": { "type": "string" } }, "required": [ "case1A", "case1B" ] }, { "$comment": "Case 2", "additionalProperties": false, "properties": { "case2A": { "type": "string" }, "case2B": { "type": "string" } }, "required": [ "case2A", "case2B" ] }, { "$comment": "Case 3", "additionalProperties": false, "properties": { "case3A": { "type": "string" }, "case3B": { "type": "string" } }, "required": [ "case3A", "case3B" ] } ] } JSON; $validator = new Validator(); $data = json_decode($dataJson); $schema = $validator->loader()->loadObjectSchema(json_decode($schemaJson)); /** @var ValidationResult $result */ $result = $validator->validate($data, $schema); if ($result->isValid()) { echo "Valid", PHP_EOL; } else { // Print errors echo "Error: "; print_r((new ErrorFormatter())->format($result->error())); foreach ($result->error()->subErrors() as $subError) { echo "Sub Error: "; /** @var \Opis\JsonSchema\Errors\ValidationError $subError */ print_r((new ErrorFormatter())->format($subError)); } } ```

Output in version 1.1.0:

$data is invalid
Error: oneOf
{
    "matched": 0
}
Sub Error: required
{
    "missing": "case1B"
}
Sub Error: required
{
    "missing": "case2A"
}
Sub Error: required
{
    "missing": "case3A"
}

Wrong output without sub errors in version 2.2.0:

Error: Array
(
    [/] => Array
        (
            [0] => The data should match exactly one schema
        )

)

If I try it in with anyOf keyword in version 2.x output is correct:

Example with anyOf in version 2.2.0 ```php use Opis\JsonSchema\{ Validator, ValidationResult, Errors\ErrorFormatter, }; $dataJson = <<<'JSON' { "case1A": "foo" } JSON; $schemaJson = <<<'JSON' { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "anyOf": [ { "$comment": "Case 1", "additionalProperties": false, "properties": { "case1A": { "type": "string" }, "case1B": { "type": "string" } }, "required": [ "case1A", "case1B" ] }, { "$comment": "Case 2", "additionalProperties": false, "properties": { "case2A": { "type": "string" }, "case2B": { "type": "string" } }, "required": [ "case2A", "case2B" ] }, { "$comment": "Case 3", "additionalProperties": false, "properties": { "case3A": { "type": "string" }, "case3B": { "type": "string" } }, "required": [ "case3A", "case3B" ] } ] } JSON; $validator = new Validator(); $data = json_decode($dataJson); $schema = $validator->loader()->loadObjectSchema(json_decode($schemaJson)); /** @var ValidationResult $result */ $result = $validator->validate($data, $schema); if ($result->isValid()) { echo "Valid", PHP_EOL; } else { // Print errors echo "Error: "; print_r((new ErrorFormatter())->format($result->error())); foreach ($result->error()->subErrors() as $subError) { echo "Sub Error: "; /** @var \Opis\JsonSchema\Errors\ValidationError $subError */ print_r((new ErrorFormatter())->format($subError)); } } ```

Output in version 2.2.0 for anyOf:

Error: Array
(
    [/] => Array
        (
            [0] => The required properties (case1B) are missing
            [1] => The required properties (case2A) are missing
            [2] => The required properties (case3A) are missing
        )

)
Sub Error: Array
(
    [/] => Array
        (
            [0] => The required properties (case1B) are missing
        )

)
Sub Error: Array
(
    [/] => Array
        (
            [0] => The required properties (case2A) are missing
        )

)
Sub Error: Array
(
    [/] => Array
        (
            [0] => The required properties (case3A) are missing
        )

)
liborm85 commented 2 years ago

Pull request with fix: https://github.com/opis/json-schema/pull/99