Merge pull request #1068 from yurikhan/violationDetails
Schema violation details
This commit is contained in:
commit
daabb88e00
280
doc/schema.md
280
doc/schema.md
@ -8,7 +8,7 @@ RapidJSON implemented a JSON Schema validator for [JSON Schema Draft v4](http://
|
|||||||
|
|
||||||
[TOC]
|
[TOC]
|
||||||
|
|
||||||
# Basic Usage {#BasicUsage}
|
# Basic Usage {#Basic}
|
||||||
|
|
||||||
First of all, you need to parse a JSON Schema into `Document`, and then compile the `Document` into a `SchemaDocument`.
|
First of all, you need to parse a JSON Schema into `Document`, and then compile the `Document` into a `SchemaDocument`.
|
||||||
|
|
||||||
@ -52,11 +52,11 @@ Some notes:
|
|||||||
* One `SchemaDocument` can be referenced by multiple `SchemaValidator`s. It will not be modified by `SchemaValidator`s.
|
* One `SchemaDocument` can be referenced by multiple `SchemaValidator`s. It will not be modified by `SchemaValidator`s.
|
||||||
* A `SchemaValidator` may be reused to validate multiple documents. To run it for other documents, call `validator.Reset()` first.
|
* A `SchemaValidator` may be reused to validate multiple documents. To run it for other documents, call `validator.Reset()` first.
|
||||||
|
|
||||||
# Validation during parsing/serialization {#ParsingSerialization}
|
# Validation during parsing/serialization {#Fused}
|
||||||
|
|
||||||
Unlike most JSON Schema validator implementations, RapidJSON provides a SAX-based schema validator. Therefore, you can parse a JSON from a stream while validating it on the fly. If the validator encounters a JSON value that invalidates the supplied schema, the parsing will be terminated immediately. This design is especially useful for parsing large JSON files.
|
Unlike most JSON Schema validator implementations, RapidJSON provides a SAX-based schema validator. Therefore, you can parse a JSON from a stream while validating it on the fly. If the validator encounters a JSON value that invalidates the supplied schema, the parsing will be terminated immediately. This design is especially useful for parsing large JSON files.
|
||||||
|
|
||||||
## DOM parsing {#DomParsing}
|
## DOM parsing {#DOM}
|
||||||
|
|
||||||
For using DOM in parsing, `Document` needs some preparation and finalizing tasks, in addition to receiving SAX events, thus it needs some work to route the reader, validator and the document. `SchemaValidatingReader` is a helper class that doing such work.
|
For using DOM in parsing, `Document` needs some preparation and finalizing tasks, in addition to receiving SAX events, thus it needs some work to route the reader, validator and the document. `SchemaValidatingReader` is a helper class that doing such work.
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ if (!reader.GetParseResult()) {
|
|||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
## SAX parsing {#SaxParsing}
|
## SAX parsing {#SAX}
|
||||||
|
|
||||||
For using SAX in parsing, it is much simpler. If it only need to validate the JSON without further processing, it is simply:
|
For using SAX in parsing, it is much simpler. If it only need to validate the JSON without further processing, it is simply:
|
||||||
|
|
||||||
@ -144,7 +144,7 @@ if (!d.Accept(validator)) {
|
|||||||
|
|
||||||
Of course, if your application only needs SAX-style serialization, it can simply send SAX events to `SchemaValidator` instead of `Writer`.
|
Of course, if your application only needs SAX-style serialization, it can simply send SAX events to `SchemaValidator` instead of `Writer`.
|
||||||
|
|
||||||
# Remote Schema {#RemoteSchema}
|
# Remote Schema {#Remote}
|
||||||
|
|
||||||
JSON Schema supports [`$ref` keyword](http://spacetelescope.github.io/understanding-json-schema/structuring.html), which is a [JSON pointer](doc/pointer.md) referencing to a local or remote schema. Local pointer is prefixed with `#`, while remote pointer is an relative or absolute URI. For example:
|
JSON Schema supports [`$ref` keyword](http://spacetelescope.github.io/understanding-json-schema/structuring.html), which is a [JSON pointer](doc/pointer.md) referencing to a local or remote schema. Local pointer is prefixed with `#`, while remote pointer is an relative or absolute URI. For example:
|
||||||
|
|
||||||
@ -176,7 +176,7 @@ The failed test is "changed scope ref invalid" of "change resolution scope" in `
|
|||||||
|
|
||||||
Besides, the `format` schema keyword for string values is ignored, since it is not required by the specification.
|
Besides, the `format` schema keyword for string values is ignored, since it is not required by the specification.
|
||||||
|
|
||||||
## Regular Expression {#RegEx}
|
## Regular Expression {#Regex}
|
||||||
|
|
||||||
The schema keyword `pattern` and `patternProperties` uses regular expression to match the required pattern.
|
The schema keyword `pattern` and `patternProperties` uses regular expression to match the required pattern.
|
||||||
|
|
||||||
@ -235,3 +235,271 @@ On a Mac Book Pro (2.8 GHz Intel Core i7), the following results are collected.
|
|||||||
|[`jayschema`](https://github.com/natesilva/jayschema)|0.1%|21 (± 1.14%)|
|
|[`jayschema`](https://github.com/natesilva/jayschema)|0.1%|21 (± 1.14%)|
|
||||||
|
|
||||||
That is, RapidJSON is about 1.5x faster than the fastest JavaScript library (ajv). And 1400x faster than the slowest one.
|
That is, RapidJSON is about 1.5x faster than the fastest JavaScript library (ajv). And 1400x faster than the slowest one.
|
||||||
|
|
||||||
|
# Schema violation reporting {#Reporting}
|
||||||
|
|
||||||
|
(Unreleased as of 2017-09-20)
|
||||||
|
|
||||||
|
When validating an instance against a JSON Schema,
|
||||||
|
it is often desirable to report not only whether the instance is valid,
|
||||||
|
but also the ways in which it violates the schema.
|
||||||
|
|
||||||
|
The `SchemaValidator` class
|
||||||
|
collects errors encountered during validation
|
||||||
|
into a JSON `Value`.
|
||||||
|
This error object can then be accessed as `validator.GetError()`.
|
||||||
|
|
||||||
|
The structure of the error object is subject to change
|
||||||
|
in future versions of RapidJSON,
|
||||||
|
as there is no standard schema for violations.
|
||||||
|
The details below this point are provisional only.
|
||||||
|
|
||||||
|
## General provisions {#ReportingGeneral}
|
||||||
|
|
||||||
|
Validation of an instance value against a schema
|
||||||
|
produces an error value.
|
||||||
|
The error value is always an object.
|
||||||
|
An empty object `{}` indicates the instance is valid.
|
||||||
|
|
||||||
|
* The name of each member
|
||||||
|
corresponds to the JSON Schema keyword that is violated.
|
||||||
|
* The value is either an object describing a single violation,
|
||||||
|
or an array of such objects.
|
||||||
|
|
||||||
|
Each violation object contains two string-valued members
|
||||||
|
named `instanceRef` and `schemaRef`.
|
||||||
|
`instanceRef` contains the URI fragment serialization
|
||||||
|
of a JSON Pointer to the instance subobject
|
||||||
|
in which the violation was detected.
|
||||||
|
`schemaRef` contains the URI of the schema
|
||||||
|
and the fragment serialization of a JSON Pointer
|
||||||
|
to the subschema that was violated.
|
||||||
|
|
||||||
|
Individual violation objects can contain other keyword-specific members.
|
||||||
|
These are detailed further.
|
||||||
|
|
||||||
|
For example, validating this instance:
|
||||||
|
|
||||||
|
~~~json
|
||||||
|
{"numbers": [1, 2, "3", 4, 5]}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
against this schema:
|
||||||
|
|
||||||
|
~~~json
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"numbers": {"$ref": "numbers.schema.json"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
where `numbers.schema.json` refers
|
||||||
|
(via a suitable `IRemoteSchemaDocumentProvider`)
|
||||||
|
to this schema:
|
||||||
|
|
||||||
|
~~~json
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "number"}
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
produces the following error object:
|
||||||
|
|
||||||
|
~~~json
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"instanceRef": "#/numbers/2",
|
||||||
|
"schemaRef": "numbers.schema.json#/items",
|
||||||
|
"expected": ["number"],
|
||||||
|
"actual": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## Validation keywords for numbers {#Numbers}
|
||||||
|
|
||||||
|
### multipleOf {#multipleof}
|
||||||
|
|
||||||
|
* `expected`: required number strictly greater than 0.
|
||||||
|
The value of the `multipleOf` keyword specified in the schema.
|
||||||
|
* `actual`: required number.
|
||||||
|
The instance value.
|
||||||
|
|
||||||
|
### maximum {#maximum}
|
||||||
|
|
||||||
|
* `expected`: required number.
|
||||||
|
The value of the `maximum` keyword specified in the schema.
|
||||||
|
* `exclusiveMaximum`: optional boolean.
|
||||||
|
This will be true if the schema specified `"exclusiveMaximum": true`,
|
||||||
|
and will be omitted otherwise.
|
||||||
|
* `actual`: required number.
|
||||||
|
The instance value.
|
||||||
|
|
||||||
|
### minimum {#minimum}
|
||||||
|
|
||||||
|
* `expected`: required number.
|
||||||
|
The value of the `minimum` keyword specified in the schema.
|
||||||
|
* `exclusiveMinimum`: optional boolean.
|
||||||
|
This will be true if the schema specified `"exclusiveMinimum": true`,
|
||||||
|
and will be omitted otherwise.
|
||||||
|
* `actual`: required number.
|
||||||
|
The instance value.
|
||||||
|
|
||||||
|
## Validation keywords for strings {#Strings}
|
||||||
|
|
||||||
|
### maxLength {#maxLength}
|
||||||
|
|
||||||
|
* `expected`: required number greater than or equal to 0.
|
||||||
|
The value of the `maxLength` keyword specified in the schema.
|
||||||
|
* `actual`: required string.
|
||||||
|
The instance value.
|
||||||
|
|
||||||
|
### minLength {#minLength}
|
||||||
|
|
||||||
|
* `expected`: required number greater than or equal to 0.
|
||||||
|
The value of the `minLength` keyword specified in the schema.
|
||||||
|
* `actual`: required string.
|
||||||
|
The instance value.
|
||||||
|
|
||||||
|
### pattern {#pattern}
|
||||||
|
|
||||||
|
* `actual`: required string.
|
||||||
|
The instance value.
|
||||||
|
|
||||||
|
(The expected pattern is not reported
|
||||||
|
because the internal representation in `SchemaDocument`
|
||||||
|
does not store the pattern in original string form.)
|
||||||
|
|
||||||
|
## Validation keywords for arrays {#Arrays}
|
||||||
|
|
||||||
|
### additionalItems {#additionalItems}
|
||||||
|
|
||||||
|
This keyword is reported
|
||||||
|
when the value of `items` schema keyword is an array,
|
||||||
|
the value of `additionalItems` is `false`,
|
||||||
|
and the instance is an array
|
||||||
|
with more items than specified in the `items` array.
|
||||||
|
|
||||||
|
* `disallowed`: required integer greater than or equal to 0.
|
||||||
|
The index of the first item that has no corresponding schema.
|
||||||
|
|
||||||
|
### maxItems and minItems {#maxItems-minItems}
|
||||||
|
|
||||||
|
* `expected`: required integer greater than or equal to 0.
|
||||||
|
The value of `maxItems` (respectively, `minItems`)
|
||||||
|
specified in the schema.
|
||||||
|
* `actual`: required integer greater than or equal to 0.
|
||||||
|
Number of items in the instance array.
|
||||||
|
|
||||||
|
### uniqueItems {#uniqueItems}
|
||||||
|
|
||||||
|
* `duplicates`: required array
|
||||||
|
whose items are integers greater than or equal to 0.
|
||||||
|
Indices of items of the instance that are equal.
|
||||||
|
|
||||||
|
(RapidJSON only reports the first two equal items,
|
||||||
|
for performance reasons.)
|
||||||
|
|
||||||
|
## Validation keywords for objects
|
||||||
|
|
||||||
|
### maxProperties and minProperties {#maxProperties-minProperties}
|
||||||
|
|
||||||
|
* `expected`: required integer greater than or equal to 0.
|
||||||
|
The value of `maxProperties` (respectively, `minProperties`)
|
||||||
|
specified in the schema.
|
||||||
|
* `actual`: required integer greater than or equal to 0.
|
||||||
|
Number of properties in the instance object.
|
||||||
|
|
||||||
|
### required {#required}
|
||||||
|
|
||||||
|
* `missing`: required array of one or more unique strings.
|
||||||
|
The names of properties
|
||||||
|
that are listed in the value of the `required` schema keyword
|
||||||
|
but not present in the instance object.
|
||||||
|
|
||||||
|
### additionalProperties {#additionalProperties}
|
||||||
|
|
||||||
|
This keyword is reported
|
||||||
|
when the schema specifies `additionalProperties: false`
|
||||||
|
and the name of a property of the instance is
|
||||||
|
neither listed in the `properties` keyword
|
||||||
|
nor matches any regular expression in the `patternProperties` keyword.
|
||||||
|
|
||||||
|
* `disallowed`: required string.
|
||||||
|
Name of the offending property of the instance.
|
||||||
|
|
||||||
|
(For performance reasons,
|
||||||
|
RapidJSON only reports the first such property encountered.)
|
||||||
|
|
||||||
|
### dependencies {#dependencies}
|
||||||
|
|
||||||
|
* `errors`: required object with one or more properties.
|
||||||
|
Names and values of its properties are described below.
|
||||||
|
|
||||||
|
Recall that JSON Schema Draft 04 supports
|
||||||
|
*schema dependencies*,
|
||||||
|
where presence of a named *controlling* property
|
||||||
|
requires the instance object to be valid against a subschema,
|
||||||
|
and *property dependencies*,
|
||||||
|
where presence of a controlling property
|
||||||
|
requires other *dependent* properties to be also present.
|
||||||
|
|
||||||
|
For a violated schema dependency,
|
||||||
|
`errors` will contain a property
|
||||||
|
with the name of the controlling property
|
||||||
|
and its value will be the error object
|
||||||
|
produced by validating the instance object
|
||||||
|
against the dependent schema.
|
||||||
|
|
||||||
|
For a violated property dependency,
|
||||||
|
`errors` will contain a property
|
||||||
|
with the name of the controlling property
|
||||||
|
and its value will be an array of one or more unique strings
|
||||||
|
listing the missing dependent properties.
|
||||||
|
|
||||||
|
## Validation keywords for any instance type {#AnyTypes}
|
||||||
|
|
||||||
|
### enum {#enum}
|
||||||
|
|
||||||
|
This keyword has no additional properties
|
||||||
|
beyond `instanceRef` and `schemaRef`.
|
||||||
|
|
||||||
|
* The allowed values are not listed
|
||||||
|
because `SchemaDocument` does not store them in original form.
|
||||||
|
* The violating value is not reported
|
||||||
|
because it might be unwieldy.
|
||||||
|
|
||||||
|
If you need to report these details to your users,
|
||||||
|
you can access the necessary information
|
||||||
|
by following `instanceRef` and `schemaRef`.
|
||||||
|
|
||||||
|
### type {#type}
|
||||||
|
|
||||||
|
* `expected`: required array of one or more unique strings,
|
||||||
|
each of which is one of the seven primitive types
|
||||||
|
defined by the JSON Schema Draft 04 Core specification.
|
||||||
|
Lists the types allowed by the `type` schema keyword.
|
||||||
|
* `actual`: required string, also one of seven primitive types.
|
||||||
|
The primitive type of the instance.
|
||||||
|
|
||||||
|
### allOf, anyOf, and oneOf {#allOf-anyOf-oneOf}
|
||||||
|
|
||||||
|
* `errors`: required array of at least one object.
|
||||||
|
There will be as many items as there are subschemas
|
||||||
|
in the `allOf`, `anyOf` or `oneOf` schema keyword, respectively.
|
||||||
|
Each item will be the error value
|
||||||
|
produced by validating the instance
|
||||||
|
against the corresponding subschema.
|
||||||
|
|
||||||
|
For `allOf`, at least one error value will be non-empty.
|
||||||
|
For `anyOf`, all error values will be non-empty.
|
||||||
|
For `oneOf`, either all error values will be non-empty,
|
||||||
|
or more than one will be empty.
|
||||||
|
|
||||||
|
### not {#not}
|
||||||
|
|
||||||
|
This keyword has no additional properties
|
||||||
|
apart from `instanceRef` and `schemaRef`.
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "rapidjson/filereadstream.h"
|
#include "rapidjson/filereadstream.h"
|
||||||
#include "rapidjson/schema.h"
|
#include "rapidjson/schema.h"
|
||||||
#include "rapidjson/stringbuffer.h"
|
#include "rapidjson/stringbuffer.h"
|
||||||
|
#include "rapidjson/prettywriter.h"
|
||||||
|
|
||||||
using namespace rapidjson;
|
using namespace rapidjson;
|
||||||
|
|
||||||
@ -67,6 +68,11 @@ int main(int argc, char *argv[]) {
|
|||||||
sb.Clear();
|
sb.Clear();
|
||||||
validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);
|
validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);
|
||||||
fprintf(stderr, "Invalid document: %s\n", sb.GetString());
|
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());
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include "document.h"
|
#include "document.h"
|
||||||
#include "pointer.h"
|
#include "pointer.h"
|
||||||
|
#include "stringbuffer.h"
|
||||||
#include <cmath> // abs, floor
|
#include <cmath> // abs, floor
|
||||||
|
|
||||||
#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
|
#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
|
||||||
@ -157,6 +158,62 @@ public:
|
|||||||
virtual void FreeState(void* p) = 0;
|
virtual void FreeState(void* p) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// IValidationErrorHandler
|
||||||
|
|
||||||
|
template <typename SchemaType>
|
||||||
|
class IValidationErrorHandler {
|
||||||
|
public:
|
||||||
|
typedef typename SchemaType::Ch Ch;
|
||||||
|
typedef typename SchemaType::SValue SValue;
|
||||||
|
|
||||||
|
virtual ~IValidationErrorHandler() {}
|
||||||
|
|
||||||
|
virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0;
|
||||||
|
virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0;
|
||||||
|
virtual void NotMultipleOf(double actual, const SValue& expected) = 0;
|
||||||
|
virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0;
|
||||||
|
virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0;
|
||||||
|
virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0;
|
||||||
|
virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0;
|
||||||
|
virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0;
|
||||||
|
virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0;
|
||||||
|
|
||||||
|
virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0;
|
||||||
|
virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0;
|
||||||
|
virtual void DoesNotMatch(const Ch* str, SizeType length) = 0;
|
||||||
|
|
||||||
|
virtual void DisallowedItem(SizeType index) = 0;
|
||||||
|
virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0;
|
||||||
|
virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0;
|
||||||
|
virtual void DuplicateItems(SizeType index1, SizeType index2) = 0;
|
||||||
|
|
||||||
|
virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0;
|
||||||
|
virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0;
|
||||||
|
virtual void StartMissingProperties() = 0;
|
||||||
|
virtual void AddMissingProperty(const SValue& name) = 0;
|
||||||
|
virtual bool EndMissingProperties() = 0;
|
||||||
|
virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0;
|
||||||
|
virtual void DisallowedProperty(const Ch* name, SizeType length) = 0;
|
||||||
|
|
||||||
|
virtual void StartDependencyErrors() = 0;
|
||||||
|
virtual void StartMissingDependentProperties() = 0;
|
||||||
|
virtual void AddMissingDependentProperty(const SValue& targetName) = 0;
|
||||||
|
virtual void EndMissingDependentProperties(const SValue& sourceName) = 0;
|
||||||
|
virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0;
|
||||||
|
virtual bool EndDependencyErrors() = 0;
|
||||||
|
|
||||||
|
virtual void DisallowedValue() = 0;
|
||||||
|
virtual void StartDisallowedType() = 0;
|
||||||
|
virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0;
|
||||||
|
virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0;
|
||||||
|
virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0;
|
||||||
|
virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
|
||||||
|
virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
|
||||||
|
virtual void Disallowed() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Hasher
|
// Hasher
|
||||||
|
|
||||||
@ -261,6 +318,7 @@ template <typename SchemaDocumentType>
|
|||||||
struct SchemaValidationContext {
|
struct SchemaValidationContext {
|
||||||
typedef Schema<SchemaDocumentType> SchemaType;
|
typedef Schema<SchemaDocumentType> SchemaType;
|
||||||
typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType;
|
typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType;
|
||||||
|
typedef IValidationErrorHandler<SchemaType> ErrorHandlerType;
|
||||||
typedef typename SchemaType::ValueType ValueType;
|
typedef typename SchemaType::ValueType ValueType;
|
||||||
typedef typename ValueType::Ch Ch;
|
typedef typename ValueType::Ch Ch;
|
||||||
|
|
||||||
@ -270,8 +328,9 @@ struct SchemaValidationContext {
|
|||||||
kPatternValidatorWithAdditionalProperty
|
kPatternValidatorWithAdditionalProperty
|
||||||
};
|
};
|
||||||
|
|
||||||
SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) :
|
SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s) :
|
||||||
factory(f),
|
factory(f),
|
||||||
|
error_handler(eh),
|
||||||
schema(s),
|
schema(s),
|
||||||
valueSchema(),
|
valueSchema(),
|
||||||
invalidKeyword(),
|
invalidKeyword(),
|
||||||
@ -311,6 +370,7 @@ struct SchemaValidationContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SchemaValidatorFactoryType& factory;
|
SchemaValidatorFactoryType& factory;
|
||||||
|
ErrorHandlerType& error_handler;
|
||||||
const SchemaType* schema;
|
const SchemaType* schema;
|
||||||
const SchemaType* valueSchema;
|
const SchemaType* valueSchema;
|
||||||
const Ch* invalidKeyword;
|
const Ch* invalidKeyword;
|
||||||
@ -345,10 +405,12 @@ public:
|
|||||||
typedef SchemaValidationContext<SchemaDocumentType> Context;
|
typedef SchemaValidationContext<SchemaDocumentType> Context;
|
||||||
typedef Schema<SchemaDocumentType> SchemaType;
|
typedef Schema<SchemaDocumentType> SchemaType;
|
||||||
typedef GenericValue<EncodingType, AllocatorType> SValue;
|
typedef GenericValue<EncodingType, AllocatorType> SValue;
|
||||||
|
typedef IValidationErrorHandler<Schema> ErrorHandler;
|
||||||
friend class GenericSchemaDocument<ValueType, AllocatorType>;
|
friend class GenericSchemaDocument<ValueType, AllocatorType>;
|
||||||
|
|
||||||
Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) :
|
Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) :
|
||||||
allocator_(allocator),
|
allocator_(allocator),
|
||||||
|
uri_(schemaDocument->GetURI(), *allocator),
|
||||||
pointer_(p),
|
pointer_(p),
|
||||||
typeless_(schemaDocument->GetTypeless()),
|
typeless_(schemaDocument->GetTypeless()),
|
||||||
enum_(),
|
enum_(),
|
||||||
@ -598,6 +660,10 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SValue& GetURI() const {
|
||||||
|
return uri_;
|
||||||
|
}
|
||||||
|
|
||||||
const PointerType& GetPointer() const {
|
const PointerType& GetPointer() const {
|
||||||
return pointer_;
|
return pointer_;
|
||||||
}
|
}
|
||||||
@ -616,9 +682,11 @@ public:
|
|||||||
context.valueSchema = additionalItemsSchema_;
|
context.valueSchema = additionalItemsSchema_;
|
||||||
else if (additionalItems_)
|
else if (additionalItems_)
|
||||||
context.valueSchema = typeless_;
|
context.valueSchema = typeless_;
|
||||||
else
|
else {
|
||||||
|
context.error_handler.DisallowedItem(context.arrayElementIndex);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
context.valueSchema = typeless_;
|
context.valueSchema = typeless_;
|
||||||
|
|
||||||
@ -642,35 +710,45 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
|
if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
|
||||||
if (!patternValid)
|
if (!patternValid) {
|
||||||
|
context.error_handler.PropertyViolations(context.patternPropertiesValidators, count);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
|
else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
|
||||||
if (!patternValid || !otherValid)
|
if (!patternValid || !otherValid) {
|
||||||
|
context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
|
||||||
}
|
}
|
||||||
else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty)
|
}
|
||||||
|
else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty)
|
||||||
|
context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (enum_) {
|
if (enum_) {
|
||||||
const uint64_t h = context.factory.GetHashCode(context.hasher);
|
const uint64_t h = context.factory.GetHashCode(context.hasher);
|
||||||
for (SizeType i = 0; i < enumCount_; i++)
|
for (SizeType i = 0; i < enumCount_; i++)
|
||||||
if (enum_[i] == h)
|
if (enum_[i] == h)
|
||||||
goto foundEnum;
|
goto foundEnum;
|
||||||
|
context.error_handler.DisallowedValue();
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString());
|
||||||
foundEnum:;
|
foundEnum:;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allOf_.schemas)
|
if (allOf_.schemas)
|
||||||
for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
|
for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
|
||||||
if (!context.validators[i]->IsValid())
|
if (!context.validators[i]->IsValid()) {
|
||||||
|
context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString());
|
||||||
|
}
|
||||||
|
|
||||||
if (anyOf_.schemas) {
|
if (anyOf_.schemas) {
|
||||||
for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
|
for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
|
||||||
if (context.validators[i]->IsValid())
|
if (context.validators[i]->IsValid())
|
||||||
goto foundAny;
|
goto foundAny;
|
||||||
|
context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString());
|
||||||
foundAny:;
|
foundAny:;
|
||||||
}
|
}
|
||||||
@ -679,30 +757,39 @@ public:
|
|||||||
bool oneValid = false;
|
bool oneValid = false;
|
||||||
for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
|
for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
|
||||||
if (context.validators[i]->IsValid()) {
|
if (context.validators[i]->IsValid()) {
|
||||||
if (oneValid)
|
if (oneValid) {
|
||||||
|
context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
|
||||||
else
|
} else
|
||||||
oneValid = true;
|
oneValid = true;
|
||||||
}
|
}
|
||||||
if (!oneValid)
|
if (!oneValid) {
|
||||||
|
context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (not_ && context.validators[notValidatorIndex_]->IsValid())
|
if (not_ && context.validators[notValidatorIndex_]->IsValid()) {
|
||||||
|
context.error_handler.Disallowed();
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString());
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Null(Context& context) const {
|
bool Null(Context& context) const {
|
||||||
if (!(type_ & (1 << kNullSchemaType)))
|
if (!(type_ & (1 << kNullSchemaType))) {
|
||||||
|
DisallowedType(context, GetNullString());
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
||||||
|
}
|
||||||
return CreateParallelValidator(context);
|
return CreateParallelValidator(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Bool(Context& context, bool) const {
|
bool Bool(Context& context, bool) const {
|
||||||
if (!(type_ & (1 << kBooleanSchemaType)))
|
if (!(type_ & (1 << kBooleanSchemaType))) {
|
||||||
|
DisallowedType(context, GetBooleanString());
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
||||||
|
}
|
||||||
return CreateParallelValidator(context);
|
return CreateParallelValidator(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -731,8 +818,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Double(Context& context, double d) const {
|
bool Double(Context& context, double d) const {
|
||||||
if (!(type_ & (1 << kNumberSchemaType)))
|
if (!(type_ & (1 << kNumberSchemaType))) {
|
||||||
|
DisallowedType(context, GetNumberString());
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
||||||
|
}
|
||||||
|
|
||||||
if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
|
if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
|
||||||
return false;
|
return false;
|
||||||
@ -747,28 +836,38 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool String(Context& context, const Ch* str, SizeType length, bool) const {
|
bool String(Context& context, const Ch* str, SizeType length, bool) const {
|
||||||
if (!(type_ & (1 << kStringSchemaType)))
|
if (!(type_ & (1 << kStringSchemaType))) {
|
||||||
|
DisallowedType(context, GetStringString());
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
||||||
|
}
|
||||||
|
|
||||||
if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
|
if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
|
||||||
SizeType count;
|
SizeType count;
|
||||||
if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
|
if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
|
||||||
if (count < minLength_)
|
if (count < minLength_) {
|
||||||
|
context.error_handler.TooShort(str, length, minLength_);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString());
|
||||||
if (count > maxLength_)
|
}
|
||||||
|
if (count > maxLength_) {
|
||||||
|
context.error_handler.TooLong(str, length, maxLength_);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (pattern_ && !IsPatternMatch(pattern_, str, length))
|
if (pattern_ && !IsPatternMatch(pattern_, str, length)) {
|
||||||
|
context.error_handler.DoesNotMatch(str, length);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString());
|
||||||
|
}
|
||||||
|
|
||||||
return CreateParallelValidator(context);
|
return CreateParallelValidator(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StartObject(Context& context) const {
|
bool StartObject(Context& context) const {
|
||||||
if (!(type_ & (1 << kObjectSchemaType)))
|
if (!(type_ & (1 << kObjectSchemaType))) {
|
||||||
|
DisallowedType(context, GetObjectString());
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
||||||
|
}
|
||||||
|
|
||||||
if (hasDependencies_ || hasRequired_) {
|
if (hasDependencies_ || hasRequired_) {
|
||||||
context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
|
context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
|
||||||
@ -826,45 +925,65 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties
|
if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties
|
||||||
|
context.error_handler.DisallowedProperty(str, len);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EndObject(Context& context, SizeType memberCount) const {
|
bool EndObject(Context& context, SizeType memberCount) const {
|
||||||
if (hasRequired_)
|
if (hasRequired_) {
|
||||||
|
context.error_handler.StartMissingProperties();
|
||||||
for (SizeType index = 0; index < propertyCount_; index++)
|
for (SizeType index = 0; index < propertyCount_; index++)
|
||||||
if (properties_[index].required)
|
if (properties_[index].required && !context.propertyExist[index])
|
||||||
if (!context.propertyExist[index])
|
context.error_handler.AddMissingProperty(properties_[index].name);
|
||||||
|
if (context.error_handler.EndMissingProperties())
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
|
||||||
|
}
|
||||||
|
|
||||||
if (memberCount < minProperties_)
|
if (memberCount < minProperties_) {
|
||||||
|
context.error_handler.TooFewProperties(memberCount, minProperties_);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
|
||||||
|
}
|
||||||
|
|
||||||
if (memberCount > maxProperties_)
|
if (memberCount > maxProperties_) {
|
||||||
|
context.error_handler.TooManyProperties(memberCount, maxProperties_);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
|
||||||
|
}
|
||||||
|
|
||||||
if (hasDependencies_) {
|
if (hasDependencies_) {
|
||||||
for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++)
|
context.error_handler.StartDependencyErrors();
|
||||||
|
for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) {
|
||||||
|
const Property& source = properties_[sourceIndex];
|
||||||
if (context.propertyExist[sourceIndex]) {
|
if (context.propertyExist[sourceIndex]) {
|
||||||
if (properties_[sourceIndex].dependencies) {
|
if (source.dependencies) {
|
||||||
|
context.error_handler.StartMissingDependentProperties();
|
||||||
for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
|
for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
|
||||||
if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex])
|
if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex])
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
|
context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name);
|
||||||
|
context.error_handler.EndMissingDependentProperties(source.name);
|
||||||
}
|
}
|
||||||
else if (properties_[sourceIndex].dependenciesSchema)
|
else if (source.dependenciesSchema) {
|
||||||
if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid())
|
ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex];
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
|
if (!dependenciesValidator->IsValid())
|
||||||
|
context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (context.error_handler.EndDependencyErrors())
|
||||||
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StartArray(Context& context) const {
|
bool StartArray(Context& context) const {
|
||||||
if (!(type_ & (1 << kArraySchemaType)))
|
if (!(type_ & (1 << kArraySchemaType))) {
|
||||||
|
DisallowedType(context, GetArrayString());
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
||||||
|
}
|
||||||
|
|
||||||
context.arrayElementIndex = 0;
|
context.arrayElementIndex = 0;
|
||||||
context.inArray = true;
|
context.inArray = true;
|
||||||
@ -875,11 +994,15 @@ public:
|
|||||||
bool EndArray(Context& context, SizeType elementCount) const {
|
bool EndArray(Context& context, SizeType elementCount) const {
|
||||||
context.inArray = false;
|
context.inArray = false;
|
||||||
|
|
||||||
if (elementCount < minItems_)
|
if (elementCount < minItems_) {
|
||||||
|
context.error_handler.TooFewItems(elementCount, minItems_);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
|
||||||
|
}
|
||||||
|
|
||||||
if (elementCount > maxItems_)
|
if (elementCount > maxItems_) {
|
||||||
|
context.error_handler.TooManyItems(elementCount, maxItems_);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1100,15 +1223,20 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CheckInt(Context& context, int64_t i) const {
|
bool CheckInt(Context& context, int64_t i) const {
|
||||||
if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
|
if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
|
||||||
|
DisallowedType(context, GetIntegerString());
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
||||||
|
}
|
||||||
|
|
||||||
if (!minimum_.IsNull()) {
|
if (!minimum_.IsNull()) {
|
||||||
if (minimum_.IsInt64()) {
|
if (minimum_.IsInt64()) {
|
||||||
if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64())
|
if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) {
|
||||||
|
context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (minimum_.IsUint64()) {
|
else if (minimum_.IsUint64()) {
|
||||||
|
context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64()
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64()
|
||||||
}
|
}
|
||||||
else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
|
else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
|
||||||
@ -1117,9 +1245,11 @@ private:
|
|||||||
|
|
||||||
if (!maximum_.IsNull()) {
|
if (!maximum_.IsNull()) {
|
||||||
if (maximum_.IsInt64()) {
|
if (maximum_.IsInt64()) {
|
||||||
if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64())
|
if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) {
|
||||||
|
context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (maximum_.IsUint64()) { }
|
else if (maximum_.IsUint64()) { }
|
||||||
/* do nothing */ // i <= max(int64_t) < maximum_.GetUint64()
|
/* do nothing */ // i <= max(int64_t) < maximum_.GetUint64()
|
||||||
else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
|
else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
|
||||||
@ -1128,9 +1258,11 @@ private:
|
|||||||
|
|
||||||
if (!multipleOf_.IsNull()) {
|
if (!multipleOf_.IsNull()) {
|
||||||
if (multipleOf_.IsUint64()) {
|
if (multipleOf_.IsUint64()) {
|
||||||
if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0)
|
if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) {
|
||||||
|
context.error_handler.NotMultipleOf(i, multipleOf_);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
|
else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1139,14 +1271,18 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CheckUint(Context& context, uint64_t i) const {
|
bool CheckUint(Context& context, uint64_t i) const {
|
||||||
if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
|
if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
|
||||||
|
DisallowedType(context, GetIntegerString());
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
|
||||||
|
}
|
||||||
|
|
||||||
if (!minimum_.IsNull()) {
|
if (!minimum_.IsNull()) {
|
||||||
if (minimum_.IsUint64()) {
|
if (minimum_.IsUint64()) {
|
||||||
if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64())
|
if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) {
|
||||||
|
context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (minimum_.IsInt64())
|
else if (minimum_.IsInt64())
|
||||||
/* do nothing */; // i >= 0 > minimum.Getint64()
|
/* do nothing */; // i >= 0 > minimum.Getint64()
|
||||||
else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
|
else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
|
||||||
@ -1155,20 +1291,26 @@ private:
|
|||||||
|
|
||||||
if (!maximum_.IsNull()) {
|
if (!maximum_.IsNull()) {
|
||||||
if (maximum_.IsUint64()) {
|
if (maximum_.IsUint64()) {
|
||||||
if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64())
|
if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) {
|
||||||
|
context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
|
||||||
}
|
}
|
||||||
else if (maximum_.IsInt64())
|
}
|
||||||
|
else if (maximum_.IsInt64()) {
|
||||||
|
context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_
|
||||||
|
}
|
||||||
else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
|
else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!multipleOf_.IsNull()) {
|
if (!multipleOf_.IsNull()) {
|
||||||
if (multipleOf_.IsUint64()) {
|
if (multipleOf_.IsUint64()) {
|
||||||
if (i % multipleOf_.GetUint64() != 0)
|
if (i % multipleOf_.GetUint64() != 0) {
|
||||||
|
context.error_handler.NotMultipleOf(i, multipleOf_);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
|
else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1177,14 +1319,18 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CheckDoubleMinimum(Context& context, double d) const {
|
bool CheckDoubleMinimum(Context& context, double d) const {
|
||||||
if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble())
|
if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) {
|
||||||
|
context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckDoubleMaximum(Context& context, double d) const {
|
bool CheckDoubleMaximum(Context& context, double d) const {
|
||||||
if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble())
|
if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) {
|
||||||
|
context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1192,11 +1338,29 @@ private:
|
|||||||
double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
|
double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
|
||||||
double q = std::floor(a / b);
|
double q = std::floor(a / b);
|
||||||
double r = a - q * b;
|
double r = a - q * b;
|
||||||
if (r > 0.0)
|
if (r > 0.0) {
|
||||||
|
context.error_handler.NotMultipleOf(d, multipleOf_);
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DisallowedType(Context& context, const ValueType& actualType) const {
|
||||||
|
ErrorHandler& eh = context.error_handler;
|
||||||
|
eh.StartDisallowedType();
|
||||||
|
|
||||||
|
if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString());
|
||||||
|
if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString());
|
||||||
|
if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString());
|
||||||
|
if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString());
|
||||||
|
if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString());
|
||||||
|
|
||||||
|
if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString());
|
||||||
|
else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString());
|
||||||
|
|
||||||
|
eh.EndDisallowedType(actualType);
|
||||||
|
}
|
||||||
|
|
||||||
struct Property {
|
struct Property {
|
||||||
Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
|
Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
|
||||||
~Property() { AllocatorType::Free(dependencies); }
|
~Property() { AllocatorType::Free(dependencies); }
|
||||||
@ -1221,6 +1385,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
AllocatorType* allocator_;
|
AllocatorType* allocator_;
|
||||||
|
SValue uri_;
|
||||||
PointerType pointer_;
|
PointerType pointer_;
|
||||||
const SchemaType* typeless_;
|
const SchemaType* typeless_;
|
||||||
uint64_t* enum_;
|
uint64_t* enum_;
|
||||||
@ -1331,6 +1496,7 @@ public:
|
|||||||
typedef typename EncodingType::Ch Ch;
|
typedef typename EncodingType::Ch Ch;
|
||||||
typedef internal::Schema<GenericSchemaDocument> SchemaType;
|
typedef internal::Schema<GenericSchemaDocument> SchemaType;
|
||||||
typedef GenericPointer<ValueType, Allocator> PointerType;
|
typedef GenericPointer<ValueType, Allocator> PointerType;
|
||||||
|
typedef GenericValue<EncodingType, Allocator> URIType;
|
||||||
friend class internal::Schema<GenericSchemaDocument>;
|
friend class internal::Schema<GenericSchemaDocument>;
|
||||||
template <typename, typename, typename>
|
template <typename, typename, typename>
|
||||||
friend class GenericSchemaValidator;
|
friend class GenericSchemaValidator;
|
||||||
@ -1340,10 +1506,13 @@ public:
|
|||||||
Compile a JSON document into schema document.
|
Compile a JSON document into schema document.
|
||||||
|
|
||||||
\param document A JSON document as source.
|
\param document A JSON document as source.
|
||||||
|
\param uri The base URI of this schema document for purposes of violation reporting.
|
||||||
|
\param uriLength Length of \c name, in code points.
|
||||||
\param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
|
\param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
|
||||||
\param allocator An optional allocator instance for allocating memory. Can be null.
|
\param allocator An optional allocator instance for allocating memory. Can be null.
|
||||||
*/
|
*/
|
||||||
explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
|
explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0,
|
||||||
|
IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
|
||||||
remoteProvider_(remoteProvider),
|
remoteProvider_(remoteProvider),
|
||||||
allocator_(allocator),
|
allocator_(allocator),
|
||||||
ownAllocator_(),
|
ownAllocator_(),
|
||||||
@ -1355,8 +1524,11 @@ public:
|
|||||||
if (!allocator_)
|
if (!allocator_)
|
||||||
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
|
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
|
||||||
|
|
||||||
|
Ch noUri[1] = {0};
|
||||||
|
uri_.SetString(uri ? uri : noUri, uriLength, *allocator_);
|
||||||
|
|
||||||
typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType)));
|
typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType)));
|
||||||
new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0);
|
new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_);
|
||||||
|
|
||||||
// Generate root schema, it will call CreateSchema() to create sub-schemas,
|
// Generate root schema, it will call CreateSchema() to create sub-schemas,
|
||||||
// And call AddRefSchema() if there are $ref.
|
// And call AddRefSchema() if there are $ref.
|
||||||
@ -1394,7 +1566,8 @@ public:
|
|||||||
root_(rhs.root_),
|
root_(rhs.root_),
|
||||||
typeless_(rhs.typeless_),
|
typeless_(rhs.typeless_),
|
||||||
schemaMap_(std::move(rhs.schemaMap_)),
|
schemaMap_(std::move(rhs.schemaMap_)),
|
||||||
schemaRef_(std::move(rhs.schemaRef_))
|
schemaRef_(std::move(rhs.schemaRef_)),
|
||||||
|
uri_(std::move(rhs.uri_))
|
||||||
{
|
{
|
||||||
rhs.remoteProvider_ = 0;
|
rhs.remoteProvider_ = 0;
|
||||||
rhs.allocator_ = 0;
|
rhs.allocator_ = 0;
|
||||||
@ -1416,6 +1589,8 @@ public:
|
|||||||
RAPIDJSON_DELETE(ownAllocator_);
|
RAPIDJSON_DELETE(ownAllocator_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const URIType& GetURI() const { return uri_; }
|
||||||
|
|
||||||
//! Get the root schema.
|
//! Get the root schema.
|
||||||
const SchemaType& GetRoot() const { return *root_; }
|
const SchemaType& GetRoot() const { return *root_; }
|
||||||
|
|
||||||
@ -1546,6 +1721,7 @@ private:
|
|||||||
SchemaType* typeless_;
|
SchemaType* typeless_;
|
||||||
internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
|
internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
|
||||||
internal::Stack<Allocator> schemaRef_; // Stores Pointer from $ref and schema which holds the $ref
|
internal::Stack<Allocator> schemaRef_; // Stores Pointer from $ref and schema which holds the $ref
|
||||||
|
URIType uri_;
|
||||||
};
|
};
|
||||||
|
|
||||||
//! GenericSchemaDocument using Value type.
|
//! GenericSchemaDocument using Value type.
|
||||||
@ -1574,13 +1750,17 @@ template <
|
|||||||
typename StateAllocator = CrtAllocator>
|
typename StateAllocator = CrtAllocator>
|
||||||
class GenericSchemaValidator :
|
class GenericSchemaValidator :
|
||||||
public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
|
public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
|
||||||
public internal::ISchemaValidator
|
public internal::ISchemaValidator,
|
||||||
|
public internal::IValidationErrorHandler<typename SchemaDocumentType::SchemaType>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef typename SchemaDocumentType::SchemaType SchemaType;
|
typedef typename SchemaDocumentType::SchemaType SchemaType;
|
||||||
typedef typename SchemaDocumentType::PointerType PointerType;
|
typedef typename SchemaDocumentType::PointerType PointerType;
|
||||||
typedef typename SchemaType::EncodingType EncodingType;
|
typedef typename SchemaType::EncodingType EncodingType;
|
||||||
|
typedef typename SchemaType::SValue SValue;
|
||||||
typedef typename EncodingType::Ch Ch;
|
typedef typename EncodingType::Ch Ch;
|
||||||
|
typedef GenericStringRef<Ch> StringRefType;
|
||||||
|
typedef GenericValue<EncodingType, StateAllocator> ValueType;
|
||||||
|
|
||||||
//! Constructor without output handler.
|
//! Constructor without output handler.
|
||||||
/*!
|
/*!
|
||||||
@ -1602,6 +1782,9 @@ public:
|
|||||||
schemaStack_(allocator, schemaStackCapacity),
|
schemaStack_(allocator, schemaStackCapacity),
|
||||||
documentStack_(allocator, documentStackCapacity),
|
documentStack_(allocator, documentStackCapacity),
|
||||||
outputHandler_(0),
|
outputHandler_(0),
|
||||||
|
error_(kObjectType),
|
||||||
|
currentError_(),
|
||||||
|
missingDependents_(),
|
||||||
valid_(true)
|
valid_(true)
|
||||||
#if RAPIDJSON_SCHEMA_VERBOSE
|
#if RAPIDJSON_SCHEMA_VERBOSE
|
||||||
, depth_(0)
|
, depth_(0)
|
||||||
@ -1630,6 +1813,9 @@ public:
|
|||||||
schemaStack_(allocator, schemaStackCapacity),
|
schemaStack_(allocator, schemaStackCapacity),
|
||||||
documentStack_(allocator, documentStackCapacity),
|
documentStack_(allocator, documentStackCapacity),
|
||||||
outputHandler_(&outputHandler),
|
outputHandler_(&outputHandler),
|
||||||
|
error_(kObjectType),
|
||||||
|
currentError_(),
|
||||||
|
missingDependents_(),
|
||||||
valid_(true)
|
valid_(true)
|
||||||
#if RAPIDJSON_SCHEMA_VERBOSE
|
#if RAPIDJSON_SCHEMA_VERBOSE
|
||||||
, depth_(0)
|
, depth_(0)
|
||||||
@ -1648,6 +1834,9 @@ public:
|
|||||||
while (!schemaStack_.Empty())
|
while (!schemaStack_.Empty())
|
||||||
PopSchema();
|
PopSchema();
|
||||||
documentStack_.Clear();
|
documentStack_.Clear();
|
||||||
|
error_.SetObject();
|
||||||
|
currentError_.SetNull();
|
||||||
|
missingDependents_.SetNull();
|
||||||
valid_ = true;
|
valid_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1655,6 +1844,10 @@ public:
|
|||||||
// Implementation of ISchemaValidator
|
// Implementation of ISchemaValidator
|
||||||
virtual bool IsValid() const { return valid_; }
|
virtual bool IsValid() const { return valid_; }
|
||||||
|
|
||||||
|
//! Gets the error object.
|
||||||
|
ValueType& GetError() { return error_; }
|
||||||
|
const ValueType& GetError() const { return error_; }
|
||||||
|
|
||||||
//! Gets the JSON pointer pointed to the invalid schema.
|
//! Gets the JSON pointer pointed to the invalid schema.
|
||||||
PointerType GetInvalidSchemaPointer() const {
|
PointerType GetInvalidSchemaPointer() const {
|
||||||
return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer();
|
return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer();
|
||||||
@ -1670,6 +1863,188 @@ public:
|
|||||||
return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
|
return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NotMultipleOf(int64_t actual, const SValue& expected) {
|
||||||
|
AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected);
|
||||||
|
}
|
||||||
|
void NotMultipleOf(uint64_t actual, const SValue& expected) {
|
||||||
|
AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected);
|
||||||
|
}
|
||||||
|
void NotMultipleOf(double actual, const SValue& expected) {
|
||||||
|
AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected);
|
||||||
|
}
|
||||||
|
void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) {
|
||||||
|
AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected,
|
||||||
|
exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
|
||||||
|
}
|
||||||
|
void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) {
|
||||||
|
AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected,
|
||||||
|
exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
|
||||||
|
}
|
||||||
|
void AboveMaximum(double actual, const SValue& expected, bool exclusive) {
|
||||||
|
AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected,
|
||||||
|
exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
|
||||||
|
}
|
||||||
|
void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) {
|
||||||
|
AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected,
|
||||||
|
exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
|
||||||
|
}
|
||||||
|
void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) {
|
||||||
|
AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected,
|
||||||
|
exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
|
||||||
|
}
|
||||||
|
void BelowMinimum(double actual, const SValue& expected, bool exclusive) {
|
||||||
|
AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected,
|
||||||
|
exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TooLong(const Ch* str, SizeType length, SizeType expected) {
|
||||||
|
AddNumberError(SchemaType::GetMaxLengthString(),
|
||||||
|
ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
|
||||||
|
}
|
||||||
|
void TooShort(const Ch* str, SizeType length, SizeType expected) {
|
||||||
|
AddNumberError(SchemaType::GetMinLengthString(),
|
||||||
|
ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
|
||||||
|
}
|
||||||
|
void DoesNotMatch(const Ch* str, SizeType length) {
|
||||||
|
currentError_.SetObject();
|
||||||
|
currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator());
|
||||||
|
AddCurrentError(SchemaType::GetPatternString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisallowedItem(SizeType index) {
|
||||||
|
currentError_.SetObject();
|
||||||
|
currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator());
|
||||||
|
AddCurrentError(SchemaType::GetAdditionalItemsString(), true);
|
||||||
|
}
|
||||||
|
void TooFewItems(SizeType actualCount, SizeType expectedCount) {
|
||||||
|
AddNumberError(SchemaType::GetMinItemsString(),
|
||||||
|
ValueType(actualCount).Move(), SValue(expectedCount).Move());
|
||||||
|
}
|
||||||
|
void TooManyItems(SizeType actualCount, SizeType expectedCount) {
|
||||||
|
AddNumberError(SchemaType::GetMaxItemsString(),
|
||||||
|
ValueType(actualCount).Move(), SValue(expectedCount).Move());
|
||||||
|
}
|
||||||
|
void DuplicateItems(SizeType index1, SizeType index2) {
|
||||||
|
ValueType duplicates(kArrayType);
|
||||||
|
duplicates.PushBack(index1, GetStateAllocator());
|
||||||
|
duplicates.PushBack(index2, GetStateAllocator());
|
||||||
|
currentError_.SetObject();
|
||||||
|
currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator());
|
||||||
|
AddCurrentError(SchemaType::GetUniqueItemsString(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TooManyProperties(SizeType actualCount, SizeType expectedCount) {
|
||||||
|
AddNumberError(SchemaType::GetMaxPropertiesString(),
|
||||||
|
ValueType(actualCount).Move(), SValue(expectedCount).Move());
|
||||||
|
}
|
||||||
|
void TooFewProperties(SizeType actualCount, SizeType expectedCount) {
|
||||||
|
AddNumberError(SchemaType::GetMinPropertiesString(),
|
||||||
|
ValueType(actualCount).Move(), SValue(expectedCount).Move());
|
||||||
|
}
|
||||||
|
void StartMissingProperties() {
|
||||||
|
currentError_.SetArray();
|
||||||
|
}
|
||||||
|
void AddMissingProperty(const SValue& name) {
|
||||||
|
currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator());
|
||||||
|
}
|
||||||
|
bool EndMissingProperties() {
|
||||||
|
if (currentError_.Empty())
|
||||||
|
return false;
|
||||||
|
ValueType error(kObjectType);
|
||||||
|
error.AddMember(GetMissingString(), currentError_, GetStateAllocator());
|
||||||
|
currentError_ = error;
|
||||||
|
AddCurrentError(SchemaType::GetRequiredString());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) {
|
||||||
|
for (SizeType i = 0; i < count; ++i)
|
||||||
|
MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
|
||||||
|
}
|
||||||
|
void DisallowedProperty(const Ch* name, SizeType length) {
|
||||||
|
currentError_.SetObject();
|
||||||
|
currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator());
|
||||||
|
AddCurrentError(SchemaType::GetAdditionalPropertiesString(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StartDependencyErrors() {
|
||||||
|
currentError_.SetObject();
|
||||||
|
}
|
||||||
|
void StartMissingDependentProperties() {
|
||||||
|
missingDependents_.SetArray();
|
||||||
|
}
|
||||||
|
void AddMissingDependentProperty(const SValue& targetName) {
|
||||||
|
missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator());
|
||||||
|
}
|
||||||
|
void EndMissingDependentProperties(const SValue& sourceName) {
|
||||||
|
if (!missingDependents_.Empty())
|
||||||
|
currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(),
|
||||||
|
missingDependents_, GetStateAllocator());
|
||||||
|
}
|
||||||
|
void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) {
|
||||||
|
currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(),
|
||||||
|
static_cast<GenericSchemaValidator*>(subvalidator)->GetError(), GetStateAllocator());
|
||||||
|
}
|
||||||
|
bool EndDependencyErrors() {
|
||||||
|
if (currentError_.ObjectEmpty())
|
||||||
|
return false;
|
||||||
|
ValueType error(kObjectType);
|
||||||
|
error.AddMember(GetErrorsString(), currentError_, GetStateAllocator());
|
||||||
|
currentError_ = error;
|
||||||
|
AddCurrentError(SchemaType::GetDependenciesString());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisallowedValue() {
|
||||||
|
currentError_.SetObject();
|
||||||
|
AddCurrentError(SchemaType::GetEnumString());
|
||||||
|
}
|
||||||
|
void StartDisallowedType() {
|
||||||
|
currentError_.SetArray();
|
||||||
|
}
|
||||||
|
void AddExpectedType(const typename SchemaType::ValueType& expectedType) {
|
||||||
|
currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator());
|
||||||
|
}
|
||||||
|
void EndDisallowedType(const typename SchemaType::ValueType& actualType) {
|
||||||
|
ValueType error(kObjectType);
|
||||||
|
error.AddMember(GetExpectedString(), currentError_, GetStateAllocator());
|
||||||
|
error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator());
|
||||||
|
currentError_ = error;
|
||||||
|
AddCurrentError(SchemaType::GetTypeString());
|
||||||
|
}
|
||||||
|
void NotAllOf(ISchemaValidator** subvalidators, SizeType count) {
|
||||||
|
for (SizeType i = 0; i < count; ++i) {
|
||||||
|
MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void NoneOf(ISchemaValidator** subvalidators, SizeType count) {
|
||||||
|
AddErrorArray(SchemaType::GetAnyOfString(), subvalidators, count);
|
||||||
|
}
|
||||||
|
void NotOneOf(ISchemaValidator** subvalidators, SizeType count) {
|
||||||
|
AddErrorArray(SchemaType::GetOneOfString(), subvalidators, count);
|
||||||
|
}
|
||||||
|
void Disallowed() {
|
||||||
|
currentError_.SetObject();
|
||||||
|
AddCurrentError(SchemaType::GetNotString());
|
||||||
|
}
|
||||||
|
|
||||||
|
#define RAPIDJSON_STRING_(name, ...) \
|
||||||
|
static const StringRefType& Get##name##String() {\
|
||||||
|
static const Ch s[] = { __VA_ARGS__, '\0' };\
|
||||||
|
static const StringRefType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1)); \
|
||||||
|
return v;\
|
||||||
|
}
|
||||||
|
|
||||||
|
RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f')
|
||||||
|
RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f')
|
||||||
|
RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd')
|
||||||
|
RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l')
|
||||||
|
RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd')
|
||||||
|
RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g')
|
||||||
|
RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's')
|
||||||
|
RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's')
|
||||||
|
|
||||||
|
#undef RAPIDJSON_STRING_
|
||||||
|
|
||||||
#if RAPIDJSON_SCHEMA_VERBOSE
|
#if RAPIDJSON_SCHEMA_VERBOSE
|
||||||
#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \
|
#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \
|
||||||
RAPIDJSON_MULTILINEMACRO_BEGIN\
|
RAPIDJSON_MULTILINEMACRO_BEGIN\
|
||||||
@ -1761,7 +2136,7 @@ RAPIDJSON_MULTILINEMACRO_END
|
|||||||
|
|
||||||
// Implementation of ISchemaStateFactory<SchemaType>
|
// Implementation of ISchemaStateFactory<SchemaType>
|
||||||
virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
|
virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
|
||||||
return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root,
|
return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom<char>(), documentStack_.GetSize(),
|
||||||
#if RAPIDJSON_SCHEMA_VERBOSE
|
#if RAPIDJSON_SCHEMA_VERBOSE
|
||||||
depth_ + 1,
|
depth_ + 1,
|
||||||
#endif
|
#endif
|
||||||
@ -1804,6 +2179,7 @@ private:
|
|||||||
GenericSchemaValidator(
|
GenericSchemaValidator(
|
||||||
const SchemaDocumentType& schemaDocument,
|
const SchemaDocumentType& schemaDocument,
|
||||||
const SchemaType& root,
|
const SchemaType& root,
|
||||||
|
const char* basePath, size_t basePathSize,
|
||||||
#if RAPIDJSON_SCHEMA_VERBOSE
|
#if RAPIDJSON_SCHEMA_VERBOSE
|
||||||
unsigned depth,
|
unsigned depth,
|
||||||
#endif
|
#endif
|
||||||
@ -1818,11 +2194,16 @@ private:
|
|||||||
schemaStack_(allocator, schemaStackCapacity),
|
schemaStack_(allocator, schemaStackCapacity),
|
||||||
documentStack_(allocator, documentStackCapacity),
|
documentStack_(allocator, documentStackCapacity),
|
||||||
outputHandler_(0),
|
outputHandler_(0),
|
||||||
|
error_(kObjectType),
|
||||||
|
currentError_(),
|
||||||
|
missingDependents_(),
|
||||||
valid_(true)
|
valid_(true)
|
||||||
#if RAPIDJSON_SCHEMA_VERBOSE
|
#if RAPIDJSON_SCHEMA_VERBOSE
|
||||||
, depth_(depth)
|
, depth_(depth)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
if (basePath && basePathSize)
|
||||||
|
memcpy(documentStack_.template Push<char>(basePathSize), basePath, basePathSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
StateAllocator& GetStateAllocator() {
|
StateAllocator& GetStateAllocator() {
|
||||||
@ -1886,8 +2267,10 @@ private:
|
|||||||
if (!a)
|
if (!a)
|
||||||
CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
|
CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
|
||||||
for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
|
for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
|
||||||
if (itr->GetUint64() == h)
|
if (itr->GetUint64() == h) {
|
||||||
|
DuplicateItems(static_cast<SizeType>(itr - a->Begin()), a->Size());
|
||||||
RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
|
RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
|
||||||
|
}
|
||||||
a->PushBack(h, GetStateAllocator());
|
a->PushBack(h, GetStateAllocator());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1916,7 +2299,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, &schema); }
|
RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, *this, &schema); }
|
||||||
|
|
||||||
RAPIDJSON_FORCEINLINE void PopSchema() {
|
RAPIDJSON_FORCEINLINE void PopSchema() {
|
||||||
Context* c = schemaStack_.template Pop<Context>(1);
|
Context* c = schemaStack_.template Pop<Context>(1);
|
||||||
@ -1927,6 +2310,70 @@ private:
|
|||||||
c->~Context();
|
c->~Context();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddErrorLocation(ValueType& result, bool parent) {
|
||||||
|
GenericStringBuffer<EncodingType> sb;
|
||||||
|
PointerType instancePointer = GetInvalidDocumentPointer();
|
||||||
|
((parent && instancePointer.GetTokenCount() > 0)
|
||||||
|
? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1)
|
||||||
|
: instancePointer).StringifyUriFragment(sb);
|
||||||
|
ValueType instanceRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
|
||||||
|
GetStateAllocator());
|
||||||
|
result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator());
|
||||||
|
sb.Clear();
|
||||||
|
memcpy(sb.Push(CurrentSchema().GetURI().GetStringLength()),
|
||||||
|
CurrentSchema().GetURI().GetString(),
|
||||||
|
CurrentSchema().GetURI().GetStringLength() * sizeof(Ch));
|
||||||
|
GetInvalidSchemaPointer().StringifyUriFragment(sb);
|
||||||
|
ValueType schemaRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
|
||||||
|
GetStateAllocator());
|
||||||
|
result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddError(ValueType& keyword, ValueType& error) {
|
||||||
|
typename ValueType::MemberIterator member = error_.FindMember(keyword);
|
||||||
|
if (member == error_.MemberEnd())
|
||||||
|
error_.AddMember(keyword, error, GetStateAllocator());
|
||||||
|
else {
|
||||||
|
if (member->value.IsObject()) {
|
||||||
|
ValueType errors(kArrayType);
|
||||||
|
errors.PushBack(member->value, GetStateAllocator());
|
||||||
|
member->value = errors;
|
||||||
|
}
|
||||||
|
member->value.PushBack(error, GetStateAllocator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddCurrentError(const typename SchemaType::ValueType& keyword, bool parent = false) {
|
||||||
|
AddErrorLocation(currentError_, parent);
|
||||||
|
AddError(ValueType(keyword, GetStateAllocator(), false).Move(), currentError_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MergeError(ValueType& other) {
|
||||||
|
for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) {
|
||||||
|
AddError(it->name, it->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddNumberError(const typename SchemaType::ValueType& keyword, ValueType& actual, const SValue& expected,
|
||||||
|
const typename SchemaType::ValueType& (*exclusive)() = 0) {
|
||||||
|
currentError_.SetObject();
|
||||||
|
currentError_.AddMember(GetActualString(), actual, GetStateAllocator());
|
||||||
|
currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator());
|
||||||
|
if (exclusive)
|
||||||
|
currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator());
|
||||||
|
AddCurrentError(keyword);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddErrorArray(const typename SchemaType::ValueType& keyword,
|
||||||
|
ISchemaValidator** subvalidators, SizeType count) {
|
||||||
|
ValueType errors(kArrayType);
|
||||||
|
for (SizeType i = 0; i < count; ++i)
|
||||||
|
errors.PushBack(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError(), GetStateAllocator());
|
||||||
|
currentError_.SetObject();
|
||||||
|
currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator());
|
||||||
|
AddCurrentError(keyword);
|
||||||
|
}
|
||||||
|
|
||||||
const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
|
const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
|
||||||
Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
|
Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
|
||||||
const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
|
const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
|
||||||
@ -1940,6 +2387,9 @@ private:
|
|||||||
internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
|
internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
|
||||||
internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
|
internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
|
||||||
OutputHandler* outputHandler_;
|
OutputHandler* outputHandler_;
|
||||||
|
ValueType error_;
|
||||||
|
ValueType currentError_;
|
||||||
|
ValueType missingDependents_;
|
||||||
bool valid_;
|
bool valid_;
|
||||||
#if RAPIDJSON_SCHEMA_VERBOSE
|
#if RAPIDJSON_SCHEMA_VERBOSE
|
||||||
unsigned depth_;
|
unsigned depth_;
|
||||||
@ -1971,13 +2421,14 @@ class SchemaValidatingReader {
|
|||||||
public:
|
public:
|
||||||
typedef typename SchemaDocumentType::PointerType PointerType;
|
typedef typename SchemaDocumentType::PointerType PointerType;
|
||||||
typedef typename InputStream::Ch Ch;
|
typedef typename InputStream::Ch Ch;
|
||||||
|
typedef GenericValue<SourceEncoding, StackAllocator> ValueType;
|
||||||
|
|
||||||
//! Constructor
|
//! Constructor
|
||||||
/*!
|
/*!
|
||||||
\param is Input stream.
|
\param is Input stream.
|
||||||
\param sd Schema document.
|
\param sd Schema document.
|
||||||
*/
|
*/
|
||||||
SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {}
|
SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), error_(kObjectType), isValid_(true) {}
|
||||||
|
|
||||||
template <typename Handler>
|
template <typename Handler>
|
||||||
bool operator()(Handler& handler) {
|
bool operator()(Handler& handler) {
|
||||||
@ -1990,11 +2441,13 @@ public:
|
|||||||
invalidSchemaPointer_ = PointerType();
|
invalidSchemaPointer_ = PointerType();
|
||||||
invalidSchemaKeyword_ = 0;
|
invalidSchemaKeyword_ = 0;
|
||||||
invalidDocumentPointer_ = PointerType();
|
invalidDocumentPointer_ = PointerType();
|
||||||
|
error_.SetObject();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
|
invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
|
||||||
invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
|
invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
|
||||||
invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
|
invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
|
||||||
|
error_.CopyFrom(validator.GetError(), allocator_);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseResult_;
|
return parseResult_;
|
||||||
@ -2005,6 +2458,7 @@ public:
|
|||||||
const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
|
const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
|
||||||
const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
|
const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
|
||||||
const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
|
const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
|
||||||
|
const ValueType& GetError() const { return error_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
InputStream& is_;
|
InputStream& is_;
|
||||||
@ -2014,6 +2468,8 @@ private:
|
|||||||
PointerType invalidSchemaPointer_;
|
PointerType invalidSchemaPointer_;
|
||||||
const Ch* invalidSchemaKeyword_;
|
const Ch* invalidSchemaKeyword_;
|
||||||
PointerType invalidDocumentPointer_;
|
PointerType invalidDocumentPointer_;
|
||||||
|
StackAllocator allocator_;
|
||||||
|
ValueType error_;
|
||||||
bool isValid_;
|
bool isValid_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user