1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
|
// Schema Validator example
// 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");
return EXIT_FAILURE;
}
// Read a JSON schema from file into Document
Document d;
char buffer[4096];
{
FILE *fp = fopen(argv[1], "r");
if (!fp) {
printf("Schema file '%s' not found\n", argv[1]);
return -1;
}
FileReadStream fs(fp, buffer, sizeof(buffer));
d.ParseStream(fs);
if (d.HasParseError()) {
fprintf(stderr, "Schema file '%s' is not a valid JSON\n", argv[1]);
fprintf(stderr, "Error(offset %u): %s\n",
static_cast<unsigned>(d.GetErrorOffset()),
GetParseError_En(d.GetParseError()));
fclose(fp);
return EXIT_FAILURE;
}
fclose(fp);
}
// Then convert the Document into SchemaDocument
SchemaDocument sd(d);
// Use reader to parse the JSON in stdin, and forward SAX events to validator
SchemaValidator validator(sd);
Reader reader;
FileReadStream is(stdin, buffer, sizeof(buffer));
if (!reader.Parse(is, validator) && reader.GetParseErrorCode() != kParseErrorTermination) {
// Schema validator error would cause kParseErrorTermination, which will handle it in next step.
fprintf(stderr, "Input is not a valid JSON\n");
fprintf(stderr, "Error(offset %u): %s\n",
static_cast<unsigned>(reader.GetErrorOffset()),
GetParseError_En(reader.GetParseErrorCode()));
}
// Check the validation result
if (validator.IsValid()) {
printf("Input JSON is valid.\n");
return EXIT_SUCCESS;
}
else {
printf("Input JSON is invalid.\n");
StringBuffer sb;
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;
}
}
|