diff options
Diffstat (limited to '3rdparty/rapidjson/example/schemavalidator/schemavalidator.cpp')
-rw-r--r-- | 3rdparty/rapidjson/example/schemavalidator/schemavalidator.cpp | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/3rdparty/rapidjson/example/schemavalidator/schemavalidator.cpp b/3rdparty/rapidjson/example/schemavalidator/schemavalidator.cpp index ce36ea95f00..6ce3c39e597 100644 --- a/3rdparty/rapidjson/example/schemavalidator/schemavalidator.cpp +++ b/3rdparty/rapidjson/example/schemavalidator/schemavalidator.cpp @@ -2,13 +2,132 @@ // The example validates JSON text from stdin with a JSON schema specified in the argument. +#define RAPIDJSON_HAS_STDSTRING 1 + #include "rapidjson/error/en.h" #include "rapidjson/filereadstream.h" #include "rapidjson/schema.h" #include "rapidjson/stringbuffer.h" +#include "rapidjson/prettywriter.h" +#include <string> +#include <iostream> +#include <sstream> using namespace rapidjson; +typedef GenericValue<UTF8<>, CrtAllocator > ValueType; + +// Forward ref +static void CreateErrorMessages(const ValueType& errors, size_t depth, const char* context); + +// Convert GenericValue to std::string +static std::string GetString(const ValueType& val) { + std::ostringstream s; + if (val.IsString()) + s << val.GetString(); + else if (val.IsDouble()) + s << val.GetDouble(); + else if (val.IsUint()) + s << val.GetUint(); + else if (val.IsInt()) + s << val.GetInt(); + else if (val.IsUint64()) + s << val.GetUint64(); + else if (val.IsInt64()) + s << val.GetInt64(); + else if (val.IsBool() && val.GetBool()) + s << "true"; + else if (val.IsBool()) + s << "false"; + else if (val.IsFloat()) + s << val.GetFloat(); + return s.str(); +} + +// Create the error message for a named error +// The error object can either be empty or contain at least member properties: +// {"errorCode": <code>, "instanceRef": "<pointer>", "schemaRef": "<pointer>" } +// 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<ValidateErrorCode>(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 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::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; + 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); + } + } + } + } +} + +// Create error message for all errors in an error structure +// Context is used to indicate whether the error structure has a parent 'dependencies', 'allOf', 'anyOf' or 'oneOf' error +static void CreateErrorMessages(const ValueType& errors, size_t depth = 0, const char* context = 0) { + // Each member property contains one or more errors of a given type + for (ValueType::ConstMemberIterator errorTypeItr = errors.MemberBegin(); errorTypeItr != errors.MemberEnd(); ++errorTypeItr) { + const char* errorName = errorTypeItr->name.GetString(); + const ValueType& errorContent = errorTypeItr->value; + if (errorContent.IsArray()) { + // Member is an array where each item is an error - eg "type": [{"errorCode": ...}, {"errorCode": ...}] + for (ValueType::ConstValueIterator contentItr = errorContent.Begin(); contentItr != errorContent.End(); ++contentItr) { + HandleError(errorName, *contentItr, depth, context); + } + } else if (errorContent.IsObject()) { + // Member is an object which is a single error - eg "type": {"errorCode": ... } + HandleError(errorName, errorContent, depth, context); + } + } +} + int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage: schemavalidator schema.json < input.json\n"); @@ -64,9 +183,17 @@ int main(int argc, char *argv[]) { validator.GetInvalidSchemaPointer().StringifyUriFragment(sb); fprintf(stderr, "Invalid schema: %s\n", sb.GetString()); fprintf(stderr, "Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword()); + fprintf(stderr, "Invalid code: %d\n", validator.GetInvalidSchemaCode()); + fprintf(stderr, "Invalid message: %s\n", GetValidateError_En(validator.GetInvalidSchemaCode())); sb.Clear(); validator.GetInvalidDocumentPointer().StringifyUriFragment(sb); fprintf(stderr, "Invalid document: %s\n", sb.GetString()); + // Detailed violation report is available as a JSON value + sb.Clear(); + PrettyWriter<StringBuffer> w(sb); + validator.GetError().Accept(w); + fprintf(stderr, "Error report:\n%s\n", sb.GetString()); + CreateErrorMessages(validator.GetError()); return EXIT_FAILURE; } } |