| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- // Schema Validator example
- // The example validates JSON text from stdin with a JSON schema specified in the argument.
- #include <rtthread.h>
- #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 schema_validator(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;
- }
- }
- MSH_CMD_EXPORT(schema_validator, rapid json schema validator example);
|