pboettch / json-schema-validator

JSON schema validator for JSON for Modern C++
Other
466 stars 134 forks source link

Calling a 'definition' from a 'definition' generate an exception #155

Closed christophe-lunarg closed 2 years ago

christophe-lunarg commented 3 years ago

I am getting this on Visual Studio 2019: Exception thrown at 0x00007FFB9E72D759 in vkconfig.exe: Microsoft C++ exception: nlohmann::detail::out_of_range at memory location 0x0000007F270F3BC8.

when assigning the following schema to a validator using set_root_schema

Here is a schema that reproduces the issue. If I comment $ref": "#/definitions/status" in "settings" the issue doesn't reproduce...

{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$id": "https://aaaaaa.org/a.json",
    "title": "A",
    "description": "B",
    "additionalProperties": true,
    "definitions": {
        "status": {
            "description": "The development status of the setting ('STABLE' by default)",
            "type": "string",
            "enum": [ "ALPHA", "BETA", "STABLE", "DEPRECATED" ]
        },
        "settings": {
            "type": "array",
            "items": {
                "description": "layer setting for GROUP type",
                "type": "object",
                "additionalProperties": true,
                "properties": {
                    "status": {
                        "$ref": "#/definitions/status"
                    }
                }
            }
        }
    },
    "properties": {
        "layer": {
            "type": "object",
            "additionalProperties": true,
            "properties": {
                "status": {
                    "$ref": "#/definitions/status"
                },
                "settings": {
                    "$ref": "#/definitions/settings"
                }
            }
        }
    }
}

https://www.jsonschemavalidator.net/ claims this schema is valid against draft v7 so I am guessing there is a bug...

pboettch commented 3 years ago

It works for me using json-schema-validate.

Can you try your schema with the json-schema-validate-application included in the package?

I quickly created an instance:

{
    "layer": {
        "status": "ALPHA",
        "settings": [
            {
                "status": "BETA"
            },
            {
                "status": "UNEXPECTED"
            }
        ]
    }
}

and I get, with your schema (t.json is your schema copy-pasted):

$ ./json-schema-validate ../t.json < i.json 
ERROR: '"/layer/settings/1/status"' - '"UNEXPECTED"': instance not found in required enum
schema validation failed
christophe-lunarg commented 3 years ago

I am getting the same error than you with ./json-schema-validate. "ERROR: '"/layer/settings/1/status"' - '"UNEXPECTED"': instance not found in required enum"

This said, I am not understanding this message... status is in definitions.

christophe-lunarg commented 3 years ago

oh, I get it, you added "UNEXPECTED"

I'll look at how I use the library compared with json-schema-validate.

christophe-lunarg commented 3 years ago

I think there is something I don't understand on how you use "./json-schema-validate ../t.json < i.json", particularly "<" in the syntax.

json-schema-validate is expecting a single argument, I thought argc would be 4 with this syntax but it should 2.

I am also wondering whether the "$id" field is a problem. I am expecting this field is actually unused, but maybe not?

The way I was using, the loader was very straight forward:

json schema = ParseFile("./schema.json");
json_validator validator;
validator.set_root_schema(schema);

And on the last line, I got the out or range issue.

I tried to use the "loader" approach from json-schema-validate, but I get the same issue...

pboettch commented 3 years ago

./json-schema-validate ../t.json < i.json is a syntax in linux shells to pass the file content (here i.json) to the process's STDIN.

json-schema-validate read the JSON-instance to be validate from STDIN. The argument is the schema-filename containing JSON.

What happens in your function ParseFile().

pboettch commented 3 years ago

I'm asking because validator.set_root_schema(schema); does not validate if the JSON passed is a valid schema. So, if your JSON is read wrongly, maybe with an additional array around the object, this

christophe-lunarg commented 3 years ago

This is how it's implemented:

static json ParseFile(const char *file) {
    QFile file_schema(file);
    const bool result = file_schema.open(QIODevice::ReadOnly | QIODevice::Text);
    assert(result);
    const std::string &data = file_schema.readAll().toStdString();
    json json_schema(json::parse(data));
    file_schema.close();

    return json_schema;
}

In Visual Studio debugger, I can the json object with content, the top items of my schema being display in the debugger.

pboettch commented 3 years ago

Could you print out the content of json_schema here with std::cout for example?

christophe-lunarg commented 3 years ago

Not quite sure how to do that trivially with my setup...

If I look at the content of "data", I have:

{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$id": "https://aaaaaa.org/a.json",
    "title": "A",
    "description": "B",
    "additionalProperties": true,
    "definitions": {
        "status": {
            "description": "The development status of the setting ('STABLE' by default)",
            "type": "string",
            "enum": [ "ALPHA", "BETA", "STABLE", "DEPRECATED" ]
        },
        "settings": {
            "type": "array",
            "items": {
                "description": "layer setting for GROUP type",
                "type": "object",
                "additionalProperties": true,
                "properties": {
                    "status": {
                        "$ref": "#/definitions/status"
                    }
                }
            }
        }
    },
    "properties": {
        "layer": {
            "type": "object",
            "additionalProperties": true,
            "properties": {
                "status": {
                    "$ref": "#/definitions/status"
                },
                "settings": {
                    "$ref": "#/definitions/settings"
                }
            }
        }
    }
}

Here is the expended tree in vs debugger if that helps: image

pboettch commented 3 years ago

Just for testing purpose could try to read your file with an ifstream instead of a QFile: I'm having doubts regarding all this read and toStdString()...

std::ifstream f(file);
json json_schema;
f >> json_schema;
return json_schema;
pboettch commented 3 years ago

Especially this line, you're taking a reference on a temporary variable, I'm surprised that the compiler doesn't error-out on this one. Which C++-standard are you using.

const std::string &data = file_schema.readAll().toStdString();
christophe-lunarg commented 3 years ago
const std::string &data = file_schema.readAll().toStdString();

Is valid code in C++... It's returned const reference that are issues...

data wouldn't be valid outside the scope of ParseFile.

This behavior is valid since C++ 98.

For the purpose of, I don't know testing, I removed the reference and the behavior is the same.

pboettch commented 3 years ago

I tried your code using QFile, I had to change this to make it parse the JSON correctly:

json json_schema = json::parse(data);

Instead of using the ctor you have to assign the output of parse.

pboettch commented 3 years ago

Does using json::parse() work for you now?

christophe-lunarg commented 3 years ago

Hi again!

I used json-schema-validate and step-in with Visual Studio 2019 and I can reproduce the issue: Exception thrown at 0x00007FFCE5F24B59 in json-schema-validate.exe: Microsoft C++ exception: nlohmann::detail::out_of_range at memory location 0x000000716A6F5378.

The exception is generated by:

validator.set_root_schema(schema);

line 73 in json-schema-validate.

christophe-lunarg commented 3 years ago

To observe the issue with command line we need to modify json.hpp because the exception is captured by JSON for Modern C++: Under: std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg; Add: std::cerr << w; And

include at the top of the file.

pboettch commented 3 years ago

I don't understand what you mean by "the exception is captured by JSON for Modern C++". Could you go the nlohman github repository and paste links to his code where that change has to be made? Check here:

https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp

christophe-lunarg commented 3 years ago

Here: https://github.com/nlohmann/json/blob/7126d88803eeb9d28cc10621f01a58813d50d078/single_include/nlohmann/json.hpp#L2960

pboettch commented 2 years ago

I'm unable to reproduce this issue and the provided information is not really helping, please re-open with a full sequence of an isolated code-example.