mirror of
				https://github.com/RetroDECK/ES-DE.git
				synced 2025-04-10 19:15:13 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			199 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			199 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // 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;
 | |
|     }
 | |
| }
 | 
