diff --git a/example/schemavalidator/schemavalidator.cpp b/example/schemavalidator/schemavalidator.cpp index a29a71e..65c95ff 100644 --- a/example/schemavalidator/schemavalidator.cpp +++ b/example/schemavalidator/schemavalidator.cpp @@ -8,7 +8,6 @@ #include "rapidjson/stringbuffer.h" #include "rapidjson/prettywriter.h" #include -#include #include using namespace rapidjson; @@ -21,87 +20,90 @@ static void CreateErrorMessages(const ValueType& errors, size_t depth, const cha // Convert GenericValue to std::string static std::string GetString(const ValueType& val) { std::string str(""); - if (val.IsString()) { + if (val.IsString()) str = val.GetString(); - } else if (val.IsDouble()) { + else if (val.IsDouble()) str = std::to_string(val.GetDouble()); - } else if (val.IsUint()) { + else if (val.IsUint()) str = std::to_string(val.GetUint()); - } else if (val.IsInt()) { + else if (val.IsInt()) str = std::to_string(val.GetInt()); - } else if (val.IsUint64()) { + else if (val.IsUint64()) str = std::to_string(val.GetUint64()); - } else if (val.IsInt64()) { + else if (val.IsInt64()) str = std::to_string(val.GetInt64()); - } else if (val.IsBool()) { - str = std::to_string(val.GetBool()); - } else if (val.IsFloat()) { + else if (val.IsBool() && val.GetBool()) + str = "true"; + else if (val.IsBool()) + str = "false"; + else if (val.IsFloat()) str = std::to_string(val.GetFloat()); - } return str; } // Create the error message for a named error -// Expects the error object to contain at least member properties: -// { -// "errorCode": , -// "instanceRef": "", -// "schemaRef": "" -// } +// The error object can either be empty or contain at least member properties: +// {"errorCode": , "instanceRef": "", "schemaRef": "" } // Additional properties may be present for use as inserts. // An "errors" property may be present if there are child errors. static void HandleError(const char* errorName, const ValueType& error, size_t depth, const char* context) { + if (!error.ObjectEmpty()) { // Get error code and look up error message text (English) int code = error["errorCode"].GetInt(); std::string message(GetValidateError_En(static_cast(code))); // For each member property in the error, see if its name exists as an insert in the error message and if so replace with the stringified property value // So for example - "Number '%actual' is not a multiple of the 'multipleOf' value '%expected'." - we would expect "actual" and "expected" members. - for (ValueType::ConstMemberIterator insertsItr = error.MemberBegin(); insertsItr != error.MemberEnd(); ++insertsItr) { - std::string insertRegex("\\%"); - insertRegex += insertsItr->name.GetString(); // eg "\%actual" - if (std::regex_search(message, std::regex(insertRegex))) { - std::string insertString(""); - const ValueType &insert = insertsItr->value; - if (insert.IsArray()) { - // Member is an array so create comma-separated list of items for the insert string - for (ValueType::ConstValueIterator itemsItr = insert.Begin(); itemsItr != insert.End(); ++itemsItr) { - if (itemsItr != insert.Begin()) insertString += ","; - insertString += GetString(*itemsItr); - } - } else { - insertString += GetString(insert); - } - message = std::regex_replace(message, std::regex(insertRegex), insertString); + for (ValueType::ConstMemberIterator insertsItr = error.MemberBegin(); + insertsItr != error.MemberEnd(); ++insertsItr) { + std::string insertName("%"); + insertName += insertsItr->name.GetString(); // eg "%actual" + size_t insertPos = message.find(insertName); + if (insertPos != std::string::npos) { + std::string insertString(""); + const ValueType &insert = insertsItr->value; + if (insert.IsArray()) { + // Member is an array so create comma-separated list of items for the insert string + for (ValueType::ConstValueIterator itemsItr = insert.Begin(); itemsItr != insert.End(); ++itemsItr) { + if (itemsItr != insert.Begin()) insertString += ","; + insertString += GetString(*itemsItr); + } + } else { + insertString += GetString(insert); } + message.replace(insertPos, insertName.length(), insertString); + } } // Output error message, references, context - std::string indent(depth*2, ' '); + std::string indent(depth * 2, ' '); std::cout << indent << "Error Name: " << errorName << std::endl; std::cout << indent << "Message: " << message.c_str() << std::endl; std::cout << indent << "Instance: " << error["instanceRef"].GetString() << std::endl; std::cout << indent << "Schema: " << error["schemaRef"].GetString() << std::endl; - if (depth > 0 ) std::cout << indent << "Context: " << context << std::endl; + if (depth > 0) std::cout << indent << "Context: " << context << std::endl; std::cout << std::endl; // If child errors exist, apply the process recursively to each error structure. // This occurs for "oneOf", "allOf", "anyOf" and "dependencies" errors, so pass the error name as context. if (error.HasMember("errors")) { - depth++; - const ValueType& childErrors = error["errors"]; - if (childErrors.IsArray()) { - // Array - each item is an error structure - example - // "anyOf": {"errorCode": ..., "errors":[{"pattern": {"errorCode\": ...\"}}, {"pattern": {"errorCode\": ...}}] - for (ValueType::ConstValueIterator errorsItr = childErrors.Begin(); errorsItr != childErrors.End(); ++errorsItr) { - CreateErrorMessages(*errorsItr, depth, errorName); - } - } else if (childErrors.IsObject()) { - // Object - each member is an error structure - example - // "dependencies": {"errorCode": ..., "errors": {"address": {"required": {"errorCode": ...}}, "name": {"required": {"errorCode": ...}}} - for (ValueType::ConstMemberIterator propsItr = childErrors.MemberBegin(); propsItr != childErrors.MemberEnd(); ++propsItr) { - CreateErrorMessages(propsItr->value, depth, errorName); - } + depth++; + const ValueType &childErrors = error["errors"]; + if (childErrors.IsArray()) { + // Array - each item is an error structure - example + // "anyOf": {"errorCode": ..., "errors":[{"pattern": {"errorCode\": ...\"}}, {"pattern": {"errorCode\": ...}}] + for (ValueType::ConstValueIterator errorsItr = childErrors.Begin(); + errorsItr != childErrors.End(); ++errorsItr) { + CreateErrorMessages(*errorsItr, depth, errorName); } + } else if (childErrors.IsObject()) { + // Object - each member is an error structure - example + // "dependencies": {"errorCode": ..., "errors": {"address": {"required": {"errorCode": ...}}, "name": {"required": {"errorCode": ...}}} + for (ValueType::ConstMemberIterator propsItr = childErrors.MemberBegin(); + propsItr != childErrors.MemberEnd(); ++propsItr) { + CreateErrorMessages(propsItr->value, depth, errorName); + } + } } + } } // Create error message for all errors in an error structure diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index b23a04d..d1173f2 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -471,7 +471,6 @@ public: exclusiveMaximum_(false), defaultValueLength_(0) { - //std::cout << "Schema constructor " << schemaDocument << std::endl; // SMH typedef typename ValueType::ConstValueIterator ConstValueIterator; typedef typename ValueType::ConstMemberIterator ConstMemberIterator; @@ -903,7 +902,6 @@ public: } bool StartObject(Context& context) const { - //std::cout << " schema StartObject" << std::endl; // SMH if (!(type_ & (1 << kObjectSchemaType))) { DisallowedType(context, GetObjectString()); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); @@ -925,7 +923,6 @@ public: } bool Key(Context& context, const Ch* str, SizeType len, bool) const { - //std::cout << " schema Key" << std::endl; // SMH if (patternProperties_) { context.patternPropertiesSchemaCount = 0; for (SizeType i = 0; i < patternPropertyCount_; i++) @@ -977,7 +974,6 @@ public: } bool EndObject(Context& context, SizeType memberCount) const { - //std::cout << " schema EndObject with members " << memberCount << std::endl; // SMH if (hasRequired_) { context.error_handler.StartMissingProperties(); for (SizeType index = 0; index < propertyCount_; index++) @@ -1025,7 +1021,6 @@ public: } bool StartArray(Context& context) const { - //std::cout << " schema StartArray" << std::endl; // SMH context.arrayElementIndex = 0; context.inArray = true; // Ensure we note that we are in an array @@ -1038,7 +1033,6 @@ public: } bool EndArray(Context& context, SizeType elementCount) const { - //std::cout << " schema EndArray" << std::endl; // SMH context.inArray = false; if (elementCount < minItems_) { @@ -1613,7 +1607,6 @@ public: schemaMap_(allocator, kInitialSchemaMapSize), schemaRef_(allocator, kInitialSchemaRefSize) { - //std::cout << "schema document constructor " << root_ << std::endl; // SMH if (!allocator_) ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); @@ -1884,7 +1877,6 @@ public: , depth_(0) #endif { - //std::cout << "validator constructor" << std::endl; // SMH } //! Constructor with output handler. @@ -1917,7 +1909,6 @@ public: , depth_(0) #endif { - //std::cout << "validator constructor with handler" << std::endl; // SMH } //! Destructor. @@ -2154,7 +2145,7 @@ public: AddCurrentError(kValidateErrorType); } void NotAllOf(ISchemaValidator** subvalidators, SizeType count) { - // Treat allOf like oneOf and anyOf for clarity + // Treat allOf like oneOf and anyOf to match https://rapidjson.org/md_doc_schema.html#allOf-anyOf-oneOf AddErrorArray(kValidateErrorAllOf, subvalidators, count); //for (SizeType i = 0; i < count; ++i) { // MergeError(static_cast(subvalidators[i])->GetError()); @@ -2211,7 +2202,6 @@ RAPIDJSON_MULTILINEMACRO_END #define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ for (Context* context = schemaStack_.template Bottom(); context != schemaStack_.template End(); context++) {\ - /*std::cout << " ++Parallel context: " << context << std::endl;*/\ if (context->hasher)\ static_cast(context->hasher)->method arg2;\ if (context->validators)\ @@ -2224,11 +2214,9 @@ RAPIDJSON_MULTILINEMACRO_END #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ valid_ = (EndValue() || GetContinueOnErrors()) && (!outputHandler_ || outputHandler_->method arg2);\ - /*std::cout << "### EndValue returns " << valid_ << std::endl;*/\ return valid_; #define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ - /*std::cout << "validator Value " << this << std::endl;*/\ RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) @@ -2246,14 +2234,12 @@ RAPIDJSON_MULTILINEMACRO_END { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } bool StartObject() { - //std::cout << "validator StartObject " << this << std::endl; // SMH RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); return valid_ = !outputHandler_ || outputHandler_->StartObject(); } bool Key(const Ch* str, SizeType len, bool copy) { - //std::cout << "validator Key: " << str << " " << this << (valid_ ? " true" : " false") << std::endl; // SMH if (!valid_) return false; AppendToken(str, len); if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) return valid_ = false; @@ -2262,7 +2248,6 @@ RAPIDJSON_MULTILINEMACRO_END } bool EndObject(SizeType memberCount) { - //std::cout << "validator EndObject " << this << std::endl; // SMH if (!valid_) return false; RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) return valid_ = false; @@ -2270,14 +2255,12 @@ RAPIDJSON_MULTILINEMACRO_END } bool StartArray() { - //std::cout << "validator StartArray " << this << std::endl; // SMH RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); return valid_ = !outputHandler_ || outputHandler_->StartArray(); } bool EndArray(SizeType elementCount) { - //std::cout << "validator EndArray " << this << std::endl; // SMH if (!valid_) return false; RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) return valid_ = false; @@ -2297,7 +2280,6 @@ RAPIDJSON_MULTILINEMACRO_END #endif &GetStateAllocator()); sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~kValidateContinueOnErrorFlag); - //std::cout << "***** New validator ***** " << sv << " " << sv->GetValidateFlags() << std::endl; return sv; } @@ -2520,7 +2502,6 @@ private: } void AddCurrentError(const ValidateErrorCode code, bool parent = false) { - //std::cout << "==== AddCurrentError ======= " << SchemaType::GetValidateErrorKeyword(code).GetString() << std::endl; AddErrorCode(currentError_, code); AddErrorInstanceLocation(currentError_, parent); AddErrorSchemaLocation(currentError_);