Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Oneof/AnyOf/AllOf applied to the properties of an object #393

Closed
CMCDragonkai opened this issue Mar 17, 2017 · 18 comments
Closed

Oneof/AnyOf/AllOf applied to the properties of an object #393

CMCDragonkai opened this issue Mar 17, 2017 · 18 comments

Comments

@CMCDragonkai
Copy link

CMCDragonkai commented Mar 17, 2017

I have tried these schemas based on these answers:

But I can't get them to work:

<?php

use JsonSchema\Validator;
use JsonSchema\Constraints\Constraint;
use JsonSchema\Exception\ValidationException;

$validator = new Validator;

$data = file_get_contents('./test.json');

$data = json_decode($data, true);

$validator->validate(
    $data,
    json_decode(json_encode([
        'type' => 'object',
        'properties' => [
            'response' => [
                'type' => 'object',
                'oneOf' => [
                    ['required' => ['error']],
                    ['required' => ['result']]
                ],
                'properties' => [
                    'error' => [
                        'type' => 'object'
                    ],
                    'result' => [
                        'type' => 'object'
                    ]
                ]
            ]
        ],
        'required' => ['response']
    ])),
    Constraint::CHECK_MODE_TYPE_CAST | Constraint::CHECK_MODE_EXCEPTIONS
);

var_dump($validator->isValid());
// JsonSchema\Exception\ValidationException with message 'Error validating /response/error: The property error is required'
<?php

use JsonSchema\Validator;
use JsonSchema\Constraints\Constraint;
use JsonSchema\Exception\ValidationException;

$validator = new Validator;

$data = file_get_contents('./test.json');

$data = json_decode($data, true);

$validator->validate(
    $data,
    json_decode(json_encode([
        'type' => 'object',
        'properties' => [
            'response' => [
                'type' => 'object',
                'oneOf' => [
                    [
                        'properties' => [
                            'error' => [
                                'type' => 'object'
                            ],
                        ]
                    ],
                    [
                        'properties' => [
                            'result' => [
                                'type' => 'object'
                            ],
                        ],
                    ]
                ]
            ]
        ],
        'required' => ['response']
    ])),
    Constraint::CHECK_MODE_TYPE_CAST | Constraint::CHECK_MODE_EXCEPTIONS
);

var_dump($validator->isValid());
// JsonSchema\Exception\ValidationException with message 'Error validating /response: Failed to match exactly one schema'

The data is like:

{
    "response": {
        "result": {}
    }
}
{
    "response": {
        "error": {}
    }
}
@erayd
Copy link
Contributor

erayd commented Mar 17, 2017

Investigating this.

For anyone else following, cleaned up schemas from the original post:

{
    "type": "object",
    "properties": {
        "response": {
            "type": "object",
            "oneOf": [
                {
                    "required": [
                        "error"
                    ]
                },
                {
                    "required": [
                        "result"
                    ]
                }
            ],
            "properties": {
                "error": {
                    "type": "object"
                },
                "result": {
                    "type": "object"
                }
            }
        }
    },
    "required": [
        "response"
    ]
}
{
    "type": "object",
    "properties": {
        "response": {
            "type": "object",
            "oneOf": [
                {
                    "properties": {
                        "error": {
                            "type": "object"
                        }
                    }
                },
                {
                    "properties": {
                        "result": {
                            "type": "object"
                        }
                    }
                }
            ]
        }
    },
    "required": [
        "response"
    ]
}

@CMCDragonkai
Copy link
Author

CMCDragonkai commented Mar 17, 2017

This one isn't working either:

<?php

use JsonSchema\Validator;
use JsonSchema\Constraints\Constraint;
use JsonSchema\Exception\ValidationException;

$validator = new Validator;

$data = file_get_contents('./test.json');

$data = json_decode($data, true);

$validator->validate(
    $data,
    json_decode(json_encode([
        'type' => 'object',
        'properties' => [
            'response' => [
                'oneOf' => [
                    ['type' => 'object', 'properties' => [
                        'error' => [
                            'type' => 'object'
                        ],
                        'required' => ['error']
                    ]],
                    ['type' => 'object', 'properties' => [
                        'result' => [
                            'type' => 'object'
                        ],
                        'required' => ['result']
                    ]]
                ]
            ]
        ],
        'required' => ['response']
    ])),
    Constraint::CHECK_MODE_TYPE_CAST | Constraint::CHECK_MODE_EXCEPTIONS
);

var_dump($validator->isValid());

@erayd
Copy link
Contributor

erayd commented Mar 17, 2017

@CMCDragonkai Your first example works fine:

<?php

require('vendor/autoload.php');

use JsonSchema\Validator;

$v = new Validator();

$schema_a = json_decode(file_get_contents('schema-a.json'));
$data_a=json_decode('{"response":{"result":{}}}');

$v->validate($data_a, $schema_a);
assert($v->isValid()); //true

@CMCDragonkai
Copy link
Author

Maybe something's going wrong with the json encode and decode that I'm using to avoid typing out (object).

@erayd
Copy link
Contributor

erayd commented Mar 17, 2017

@CMCDragonkai Your second example works fine too - it fails validation, as it should, because your input data matches both schemas in oneOf.

Your third example has an invalid schema - properties must be objects, but you have defined one named "required" as an array.

Could you please explain exactly what your issue is? Because from where I'm sitting, this doesn't look like a problem with the library, or with json_encode / json_decode. This looks like user error on your part.

You may wish to check out http://www.jsonschemavalidator.net/ to assist with writing a valid schema that fulfils your requirements.

@CMCDragonkai
Copy link
Author

CMCDragonkai commented Mar 17, 2017

Try this (this throws an error):

<?php

use JsonSchema\Validator;
use JsonSchema\Constraints\Constraint;
use JsonSchema\Exception\ValidationException;

$validator = new Validator;

$data = <<<'JSON'
{
    "response": {
        "result": {}
    }
}
JSON;

$data = json_decode($data, true);

$validator->validate(
    $data,
    (object)[
        'type' => 'object',
        'properties' => (object)[
            'response' => (object)[
                'type' => 'object',
                'oneOf' => [
                    (object)['required' => ['error']],
                    (object)['required' => ['result']]
                ],
                'properties' => (object)[
                    'error' => (object)[
                        'type' => 'object'
                    ],
                    'result' => (object)[
                        'type' => 'object'
                    ]
                ]
            ]
        ],
        'required' => ['response']
    ],
    Constraint::CHECK_MODE_TYPE_CAST | Constraint::CHECK_MODE_EXCEPTIONS
);

var_dump($validator->isValid());

Your examples doesn't apply the constraints that I'm using.

@CMCDragonkai
Copy link
Author

It's possible that because of {} gets decoded as a PHP array with the true option, and I'm looking for an object.

@CMCDragonkai
Copy link
Author

CMCDragonkai commented Mar 17, 2017

I'm on 5.1 version as well, and my most recent example really doesn't work. Even if I remove the true from json_decode and Constraint::CHECK_MODE_TYPE_CAST, it still throws an error.

@CMCDragonkai
Copy link
Author

CMCDragonkai commented Mar 17, 2017

Your third example has an invalid schema - properties must be objects, but you have defined one named "required" as an array.

My third example has properties as an object, not sure what you mean that I've 'defined one named "required" as an array'? Because I haven't done that. This is the internal-memory output from the third schema in PHP:

{#238
     +"type": "object",
     +"properties": {#246
       +"response": {#245
         +"oneOf": [
           {#241
             +"type": "object",
             +"properties": {#242
               +"error": {#243
                 +"type": "object",
               },
               +"required": [
                 "error",
               ],
             },
           },
           {#180
             +"type": "object",
             +"properties": {#244
               +"result": {#240
                 +"type": "object",
               },
               +"required": [
                 "result",
               ],
             },
           },
         ],
       },
     },
     +"required": [
       "response",
     ],
   }

None of the properties there are typed as arrays. They are all objects because of their object id.

The third example was based on your README showing this:

$jsonSchema = <<<'JSON'
{
    "type": "object",
    "properties": {
        "data": {
            "oneOf": [
                { "$ref": "#/definitions/integerData" },
                { "$ref": "#/definitions/stringData" }
            ]
        }
    },
    "required": ["data"],
    "definitions": {
        "integerData" : {
            "type": "integer",
            "minimum" : 0
        },
        "stringData" : {
            "type": "string"
        }
    }
}
JSON;

See properties -> data -> oneOf. Hence properties -> response -> oneOf in the third schema.

@CMCDragonkai
Copy link
Author

I found the problem. It's because of Constraint::CHECK_MODE_EXCEPTIONS. If you add it in, it fails on the first error. That error is that it fails one of the of oneOf. If you remove CHECK_MODE_EXCEPTIONS, it succeeds at the very end.

@CMCDragonkai
Copy link
Author

CMCDragonkai commented Mar 17, 2017

Compare:

<?php

//SUCCEEDS

use JsonSchema\Validator;
use JsonSchema\Constraints\Constraint;
use JsonSchema\Exception\ValidationException;

$validator = new Validator;

$data = <<<'JSON'
{
    "response": {
        "result": {
        }
    }
}
JSON;

$schema = <<<'JSON'
{
    "type": "object",
    "properties": {
        "response": {
            "type": "object",
            "oneOf": [
                {
                    "required": [
                        "error"
                    ]
                },
                {
                    "required": [
                        "result"
                    ]
                }
            ],
            "properties": {
                "error": {
                    "type": "object"
                },
                "result": {
                    "type": "object"
                }
            }
        }
    },
    "required": [
        "response"
    ]
}
JSON;

$data = json_decode($data);
$schema = json_decode($schema);

var_dump($data);
var_dump($schema);

$validator->validate(
    $data,
    $schema
);

var_dump($validator->isValid());
<?php

//FAILS

use JsonSchema\Validator;
use JsonSchema\Constraints\Constraint;
use JsonSchema\Exception\ValidationException;

$validator = new Validator;

$data = <<<'JSON'
{
    "response": {
        "result": {
        }
    }
}
JSON;

$schema = <<<'JSON'
{
    "type": "object",
    "properties": {
        "response": {
            "type": "object",
            "oneOf": [
                {
                    "required": [
                        "error"
                    ]
                },
                {
                    "required": [
                        "result"
                    ]
                }
            ],
            "properties": {
                "error": {
                    "type": "object"
                },
                "result": {
                    "type": "object"
                }
            }
        }
    },
    "required": [
        "response"
    ]
}
JSON;

$data = json_decode($data);
$schema = json_decode($schema);

var_dump($data);
var_dump($schema);

$validator->validate(
    $data,
    $schema,
    Constraint::CHECK_MODE_EXCEPTIONS
);

var_dump($validator->isValid());

@shmax
Copy link
Collaborator

shmax commented Mar 17, 2017

try:

$data = json_decode($data, false);

@CMCDragonkai
Copy link
Author

@shmax Please see my most recent example. It shows that the problem is with exception constraint, not about json decoding.

@erayd
Copy link
Contributor

erayd commented Mar 17, 2017

Thanks - I can confirm there is a problem with CHECK_MODE_EXCEPTIONS; I will fix this.

Regarding the other issues you're having - you are definitely making mistakes with some of your schemas. I strongly recommend you take advantage of http://www.jsonschemavalidator.net/; it will help you ensure that your schemas are valid.

My third example has properties as an object, not sure what you mean that I've 'defined one named "required" as an array'? Because I haven't done that.

Yes, you have done that. You have properties->required->[], which is illegal.

@CMCDragonkai
Copy link
Author

@erayd I'm sorry but I just have my Spacemacs telling me I do not have required as a property of properties. They are siblings, not parent-children relationship.

Thank you for the fix.

@erayd
Copy link
Contributor

erayd commented Mar 17, 2017

@CMCDragonkai

Look at properties>>response>>oneOf>>properties>>required here. [Ed: fixed link URL]

I have now pointed this out three times. Look at your code.

I know there is a bug with CHECK_MODE_EXCEPTIONS, however your insistence that there is no user-error here is annoying, and makes me less inclined to pay attention when you submit new issues. Please check your input before claiming it's perfect, especially when someone has already told you multiple times it's not.

@CMCDragonkai
Copy link
Author

@erayd I see it. I thought you were referring to the first "required" property, not the inner set.

@erayd
Copy link
Contributor

erayd commented Mar 17, 2017

The CHECK_MODE_EXCEPTIONS bug has been fixed in #394. Thanks for bringing it to my attention :-).

erayd added a commit to erayd/json-schema that referenced this issue Mar 17, 2017
erayd added a commit to erayd/json-schema that referenced this issue Mar 17, 2017
erayd added a commit to erayd/json-schema that referenced this issue Mar 17, 2017
bighappyface pushed a commit that referenced this issue Mar 22, 2017
* Add URI translation for retrieval & add local copies of spec schema

* Add use line for InvalidArgumentException & adjust scope (#372)

Fixes issue #371

* add quiet option (#382)

* add quiet option

* use verbose instead of quiet

* add quiet option

* always output dump-schema

* always output dump-schema-url

* fix typo and ws

* [BUGFIX] Add provided schema under a dummy / internal URI (fixes #376) (#378)

* Add provided schema under a dummy / internal URI (fixes #376)

In order to resolve internal $ref references within a user-provided
schema, SchemaStorage needs to know about the schema. As user-supplied
schemas do not have an associated URI, use a dummy / internal one instead.

* Remove dangling use

* Change URI to class constant on SchemaStorage

* Add option to disable validation of "format" constraint (#383)

* Add more unit tests (#366)

* Add test coverage for coercion API

* Complete test coverage for SchemaStorage

* Add test coverage for ObjectIterator

* Add exception test for JsonPointer

* MabeEnum\Enum appears to use singletons - add testing const

* Don't check this line for coverage

mbstring is on all test platforms, so this line will never be reached.

* Add test for TypeConstraint::validateTypeNameWording()

* Add test for exception on TypeConstraint::validateType()

* PHPunit doesn't like an explanation with its @codeCoverageIgnore...

* Add various tests for UriRetriever

* Add tests for FileGetContents

* Add tests for JsonSchema\Uri\Retrievers\Curl

* Add missing bad-syntax test file

* Restrict ignore to the exception line only

* Fix exception scope

* Allow the schema to be an associative array (#389)

* Allow the schema to be an associative array

Implements #388.

* Use json_decode(json_encode()) for array -> object cast

* Skip exception check on PHP versions < 5.5.0

* Skip test on HHVM, as it's happy to encode resources

* Enable FILTER_FLAG_EMAIL_UNICODE for email format if present (#398)

* Don't throw exceptions until after checking anyOf / oneOf (#394)

Fixes #393

* Fix infinite recursion on some schemas when setting defaults (#359) (#365)

* Don't try to fetch files that don't exist

Throws an exception when the ref can't be resolved to a useful file URI,
rather than waiting for something further down the line to fail after
the fact.

* Refactor defaults code to use LooseTypeCheck where appropriate

* Test for not treating non-containers like arrays

* Update comments

* Rename variable for clarity

* Add CHECK_MODE_ONLY_REQUIRED_DEFAULTS

If CHECK_MODE_ONLY_REQUIRED_DEFAULTS is set, then only apply defaults
if they are marked as required.

* Workaround for $this scope issue on PHP-5.3

* Fix infinite recursion via $ref when applying defaults

* Add missing second test for array case

* Add test for setting a default value for null

* Also fix infinite recursion via $ref for array defaults

* Move nested closure into separate method

* $parentSchema will always be set when $name is, so don't check it

* Handle nulls properly - fixes issue #377

* Add option to also validate the schema (#357)

* Remove stale files from #357 (obviated by #362) (#400)

* Stop #386 sneaking in alongside another PR backport
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants