diff --git a/src/ujson/Schema.cpp b/src/ujson/Schema.cpp index 0f87f19..2039677 100644 --- a/src/ujson/Schema.cpp +++ b/src/ujson/Schema.cpp @@ -16,7 +16,9 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include #include +#include #include #include @@ -73,12 +75,11 @@ namespace ujson { //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- - Schema::Schema (jvalue& schema) - : root_schema (schema) + Schema::Schema (jvalue& root_instance) + : root_schema (root_instance) { } - //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- Schema::result_t Schema::validate (jvalue& instance) @@ -86,6 +87,38 @@ namespace ujson { return validate_impl (root_schema, instance); } + + //-------------------------------------------------------------------------- + //-------------------------------------------------------------------------- + bool Schema::add_ref_schema (const Schema& schema) + { + auto& id = find_jvalue (const_cast(schema.root_schema), "/$id"); + if (id.type() != j_string) + return false; + + ref_schemas.emplace (id.str(), Schema(schema)); + ref_cache.clear (); + return true; + } + + + //-------------------------------------------------------------------------- + //-------------------------------------------------------------------------- + void Schema::del_ref_schema (const std::string& id) + { + ref_schemas.erase (id); + ref_cache.clear (); + } + + + //-------------------------------------------------------------------------- + //-------------------------------------------------------------------------- + jvalue& Schema::root () + { + return root_schema; + } + + //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- static bool is_schema_type (jvalue& schema) @@ -96,24 +129,51 @@ namespace ujson { //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- - jvalue* Schema::get_ref_schema (const std::string& ref) + Schema::result_t Schema::handle_ref (const std::string& ref, jvalue& instance) { - jvalue* schema = nullptr; - std::stringstream ss (ref); - std::string part; - std::vector parts; - std::getline(ss, part, '/'); - if (part == "#") { - while (std::getline(ss, part, '/')) { - if (schema == nullptr) - schema = &(root_schema.get(part)); - else - schema = &(schema->get(part)); - if (schema->type() == j_invalid) - break; + auto entry = ref_cache.find (ref); + if (entry != ref_cache.end()) { + Schema& ref_root = entry->second.first; + jvalue& ref_schema = entry->second.second; + return ref_root.validate_impl (ref_schema, instance); + } + + std::string id; + std::string pointer; + + auto pos = ref.find ("#"); + if (pos == std::string::npos) { + pointer = ref; + }else{ + id = ref.substr (0, pos); + pointer = ref.substr (pos+1); + } + pointer.insert (0, 1, '/'); // Make sure the pointer starts with / + + if (id.empty()) { + // Reference to pointer in this schema + auto& ref_schema = find_jvalue (root_schema, pointer); + if (ref_schema.valid()) { + ref_cache.emplace (ref, + std::make_pair(std::reference_wrapper(*this), + std::reference_wrapper(ref_schema))); + } + return validate_impl (ref_schema, instance); + }else{ + // Reference to pointer in other schema + auto entry = ref_schemas.find (id); + if (entry == ref_schemas.end()) { + return err_schema; } + auto& ref_root = entry->second; + auto& ref_schema = find_jvalue (ref_root.root_schema, pointer); + if (ref_schema.valid()) { + ref_cache.emplace (ref, + std::make_pair(std::reference_wrapper(ref_root), + std::reference_wrapper(ref_schema))); + } + return ref_root.validate_impl (ref_schema, instance); } - return schema; } @@ -141,14 +201,8 @@ namespace ujson { // Handle keyword "$ref" first // auto& ref = schema.get("$ref"); - if (ref.type() != j_invalid) { - jvalue* ref_schema = get_ref_schema (ref.str()); - if (!ref_schema) - return err_schema; - vdata.result = validate_impl (*ref_schema, instance); - if (vdata.result != valid) - return vdata.result; - } + if (ref.type() != j_invalid) + return handle_ref (ref.str(), instance); // Handle the schema keywords // @@ -340,11 +394,6 @@ namespace ujson { } } - // - // Validation - // - - if (vdata.result != valid) return vdata.result; } diff --git a/src/ujson/Schema.hpp b/src/ujson/Schema.hpp index b07731f..b7727d2 100644 --- a/src/ujson/Schema.hpp +++ b/src/ujson/Schema.hpp @@ -40,20 +40,37 @@ namespace ujson { }; /** - * Constructor. + * Default constructor. */ - Schema (jvalue& schema); + Schema () = default; /** - * Destructor. + * Constructor. */ - ~Schema () = default; + Schema (jvalue& root_instance); /** * @return Schema::valid on success. */ result_t validate (jvalue& instance); + /** + * Add a schema that this schema mey refer to. + */ + bool add_ref_schema (const Schema& schema); + + /** + * Remove a schema that this schema mey refer to. + * @param id The id of the schema to remove. + */ + void del_ref_schema (const std::string& id); + + /** + * Return the root schema instance. + */ + jvalue& root (); + + private: struct vdata_t { jvalue& schema; @@ -74,9 +91,15 @@ namespace ujson { }; jvalue root_schema; - jvalue* get_ref_schema (const std::string& ref); + std::map ref_schemas; + std::map, + std::reference_wrapper>> ref_cache; + result_t validate_impl (jvalue& schema, jvalue& instance); + result_t handle_ref (const std::string& ref, jvalue& instance); + // Core - all types void handle_allOf (vdata_t& vdata, jvalue& value); void handle_anyOf (vdata_t& vdata, jvalue& value); diff --git a/utils/ujson-verify.cpp b/utils/ujson-verify.cpp index c8e1004..3e3a243 100644 --- a/utils/ujson-verify.cpp +++ b/utils/ujson-verify.cpp @@ -41,6 +41,7 @@ struct appargs_t { bool quiet; std::vector files; std::string schema_file; + std::vector ref_schema_files; appargs_t() { strict = true; @@ -59,10 +60,14 @@ static void print_usage_and_exit (std::ostream& out, int exit_code) << "Usage: " << prog_name << " [OPTIONS] [FILE...]" << endl << endl << "Options:" <