JSON schema validation with references to other schemas #557
-
Hello, I'm trying to perform JSON schema validation with references to other schemas ($ref) inside the main schema but cannot make it work. I think my URI resolver doesn't work but I don't know why. I have read this example as well as this issue. Reading the example, I don't understand how you come up with the code of the resolver, namely this part:
My main schema looks like this:
My URI resolver (not my actual implementation, just the logging part):
Whatever I try, When creating the compiled schema with I am working on providing a minimal example for this post, but it will take me some more time before it is ready. Thanks in advance |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments 11 replies
-
It's easier for me to investigate once I have your minimal example, thanks. |
Beta Was this translation helpful? Give feedback.
-
References to other schemas via the $ref keyword are resolved relative to the base URI, usually provided via the $id keyword. In your case, you don't provide a base URI, and it defaults to empty. When jsoncons attempts to resolve "./other.schema.json" relative to the base URI "", it produces "/./other.schema.json". And yes, that's a bug, you can't resolve a relative URI against another relative URI. This has been fixed on master. Note however that JSONSchema recommends including an $id property as a unique identifier for each schema. That makes it questionable to resolve everything to a relative URI and using that to locate resources. I would suggest something along the following lines, supplying the path to the root of the file directory as a parameter to the resolver, {
"$id" : "https://my_domain.com",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Title",
"type": "object",
"properties": {
"my_prop": { "$ref": "dir1/other.schema.json" },
"my_other_prop": { "$ref": "dir2/foo/bar/baz/main.schema.json" },
"$version": { "type": "integer" }
},
"additionalProperties": false
} jsoncons::ojson resolver(const jsoncons::uri& p_uri)
{
std::cout << "p_uri.base : " << p_uri.base().string() << std::endl; // https://my_domain.com/dir1/other.schema.json
std::cout << "p_uri.path : " << p_uri.path() << std::endl; // /dir1/other.schema.json
std::string my_file_path = "/path_to_root" + p_uri.path();
std::cout << "my_file_path: " << my_file_path << "\n";
// Look up file at location 'my_file_path' |
Beta Was this translation helpful? Give feedback.
-
Here's a minimalistic example that shows how to pass the root directory as a command line argument. It should work with both the latest release, 0.178.0, and master, which will be released as 1.0.0. #include <jsoncons/json.hpp>
#include <jsoncons_ext/jsonschema/jsonschema.hpp>
#include <iostream>
#include <strstream>
std::string main_schema = R"(
{
"$id" : "https://my_domain.com",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Title",
"type": "object",
"properties": {
"my_prop": { "$ref": "dir1/other.schema.json" },
"my_other_prop": { "$ref": "dir2/foo/bar/baz/main.schema.json" },
"$version": { "type": "integer" }
},
"additionalProperties": false
}
)";
jsoncons::ojson other_schema = jsoncons::ojson::parse(R"(
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Title",
"type": "object",
"properties": {
"prop_1": { "type": "string" },
"prop_2": { "type": "string" }
},
"required": ["prop_1", "prop_2"],
"additionalProperties": false
}
)");
jsoncons::ojson empty_schema = jsoncons::ojson::parse(R"(
{
}
)");
int main(int argc, char* args[])
{
if (argc < 2)
{
return 1;
}
const char* root_dir = args[1];
jsoncons::ojson schema;
try {
std::stringstream is(main_schema.c_str());
schema = jsoncons::ojson::parse(is);
auto resolver = [root_dir](const jsoncons::uri& p_uri)
{
std::string pathname = p_uri.path();
std::cout << "p_uri.base : " << p_uri.base().string() << std::endl;
std::cout << "p_uri.path : " << p_uri.path() << std::endl;
std::string my_file_path = root_dir + p_uri.path();
std::cout << "my_file_path: " << my_file_path << "\n";
// Mimics file retrieval
if (my_file_path == "/path_to_root/dir1/other.schema.json")
{
return other_schema;
}
else if (my_file_path == "/path_to_root/dir2/foo/bar/baz/main.schema.json")
{
return empty_schema;
}
else
{
std::cout << "Resolver cannot parse " << my_file_path << std::endl;
return jsoncons::ojson::null();
}
};
auto validator = jsoncons::jsonschema::make_json_schema(schema, resolver);
}
catch (const std::exception& ex) {
throw std::runtime_error("Cannot create JSON schema validator: " + std::string(ex.what()));
}
return 0;
} When run with command line argument
|
Beta Was this translation helpful? Give feedback.
-
The example I posted should work without the $id keyword in the schema using any release build, latest is 0.178.0. It works for the wrong reason (see earlier comments), but it works nonetheless. It wouldn't currently work on master but will once we add a default base uri option (should be soon.) The URI provided with a $id keyword needs to be valid in the sense that it can be parsed, but not in the sense that it corresponds to a registered domain name or web address. In your case you're only interested in the path component. The main place you'll see this URI is in the 'schemaLocation' property of validation reports. |
Beta Was this translation helpful? Give feedback.
-
I think I finally got it to work, after adapting my code, all legacy tests are passing. Thanks again for all the help! |
Beta Was this translation helpful? Give feedback.
Here's a minimalistic example that shows how to pass the root directory as a command line argument. It should work with both the latest release, 0.178.0, and master, which will be released as 1.0.0.