mirror of
https://github.com/tristanpenman/valijson.git
synced 2025-03-03 21:06:25 +01:00
Cosmetic improvements for schema_parser.hpp and validation_results.hpp
This commit is contained in:
parent
71f4cdaa84
commit
217b990b00
@ -41,16 +41,13 @@ public:
|
|||||||
kDraft7
|
kDraft7
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Version of JSON Schema that should be expected when parsing
|
|
||||||
const Version version;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct a new SchemaParser for a given version of JSON Schema
|
* @brief Construct a new SchemaParser for a given version of JSON Schema
|
||||||
*
|
*
|
||||||
* @param version Version of JSON Schema that will be expected
|
* @param version Version of JSON Schema that will be expected
|
||||||
*/
|
*/
|
||||||
explicit SchemaParser(const Version version = kDraft7)
|
explicit SchemaParser(const Version version = kDraft7)
|
||||||
: version(version) { }
|
: m_version(version) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Release memory associated with custom ConstraintBuilders
|
* @brief Release memory associated with custom ConstraintBuilders
|
||||||
@ -68,14 +65,13 @@ public:
|
|||||||
template<typename AdapterType>
|
template<typename AdapterType>
|
||||||
struct FunctionPtrs
|
struct FunctionPtrs
|
||||||
{
|
{
|
||||||
typedef typename adapters::AdapterTraits<AdapterType>::DocumentType
|
typedef typename adapters::AdapterTraits<AdapterType>::DocumentType DocumentType;
|
||||||
DocumentType;
|
|
||||||
|
|
||||||
/// Templated function pointer type for fetching remote documents
|
/// Templated function pointer type for fetching remote documents
|
||||||
typedef std::function< const DocumentType* (const std::string &uri) > FetchDoc ;
|
typedef std::function<const DocumentType* (const std::string &uri)> FetchDoc;
|
||||||
|
|
||||||
/// Templated function pointer type for freeing fetched documents
|
/// Templated function pointer type for freeing fetched documents
|
||||||
typedef std::function< void (const DocumentType *)> FreeDoc ;
|
typedef std::function<void (const DocumentType *)> FreeDoc;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,8 +92,7 @@ public:
|
|||||||
* @todo Add additional checks for key conflicts, empty keys, and
|
* @todo Add additional checks for key conflicts, empty keys, and
|
||||||
* potential restrictions relating to case sensitivity
|
* potential restrictions relating to case sensitivity
|
||||||
*/
|
*/
|
||||||
void addConstraintBuilder(const std::string &key,
|
void addConstraintBuilder(const std::string &key, const ConstraintBuilder *builder)
|
||||||
const ConstraintBuilder *builder)
|
|
||||||
{
|
{
|
||||||
constraintBuilders.push_back(std::make_pair(key, builder));
|
constraintBuilders.push_back(std::make_pair(key, builder));
|
||||||
}
|
}
|
||||||
@ -122,17 +117,14 @@ public:
|
|||||||
typename FunctionPtrs<AdapterType>::FreeDoc freeDoc = nullptr )
|
typename FunctionPtrs<AdapterType>::FreeDoc freeDoc = nullptr )
|
||||||
{
|
{
|
||||||
if ((fetchDoc == nullptr ) ^ (freeDoc == nullptr)) {
|
if ((fetchDoc == nullptr ) ^ (freeDoc == nullptr)) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Remote document fetching can't be enabled without both fetch and free functions");
|
||||||
"Remote document fetching cannot be enabled without both "
|
|
||||||
"fetch and free functions");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typename DocumentCache<AdapterType>::Type docCache;
|
typename DocumentCache<AdapterType>::Type docCache;
|
||||||
SchemaCache schemaCache;
|
SchemaCache schemaCache;
|
||||||
try {
|
try {
|
||||||
resolveThenPopulateSchema(schema, node, node, schema,
|
resolveThenPopulateSchema(schema, node, node, schema, opt::optional<std::string>(), "", fetchDoc, nullptr,
|
||||||
opt::optional<std::string>(), "",
|
nullptr, docCache, schemaCache);
|
||||||
fetchDoc, nullptr, nullptr, docCache, schemaCache);
|
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
freeDocumentCache<AdapterType>(docCache, freeDoc);
|
freeDocumentCache<AdapterType>(docCache, freeDoc);
|
||||||
throw;
|
throw;
|
||||||
@ -151,8 +143,7 @@ private:
|
|||||||
template<typename AdapterType>
|
template<typename AdapterType>
|
||||||
struct DocumentCache
|
struct DocumentCache
|
||||||
{
|
{
|
||||||
typedef typename adapters::AdapterTraits<AdapterType>::DocumentType
|
typedef typename adapters::AdapterTraits<AdapterType>::DocumentType DocumentType;
|
||||||
DocumentType;
|
|
||||||
|
|
||||||
typedef std::map<std::string, const DocumentType*> Type;
|
typedef std::map<std::string, const DocumentType*> Type;
|
||||||
};
|
};
|
||||||
@ -204,16 +195,15 @@ private:
|
|||||||
* portions of URI provided by the resolution scope.
|
* portions of URI provided by the resolution scope.
|
||||||
*/
|
*/
|
||||||
virtual opt::optional<std::string> findAbsoluteDocumentUri(
|
virtual opt::optional<std::string> findAbsoluteDocumentUri(
|
||||||
const opt::optional<std::string> resolutionScope,
|
const opt::optional<std::string>& resolutionScope,
|
||||||
const opt::optional<std::string> documentUri)
|
const opt::optional<std::string>& documentUri)
|
||||||
{
|
{
|
||||||
if (resolutionScope) {
|
if (resolutionScope) {
|
||||||
if (documentUri) {
|
if (documentUri) {
|
||||||
if (internal::uri::isUriAbsolute(*documentUri)) {
|
if (internal::uri::isUriAbsolute(*documentUri)) {
|
||||||
return *documentUri;
|
return *documentUri;
|
||||||
} else {
|
} else {
|
||||||
return internal::uri::resolveRelativeUri(
|
return internal::uri::resolveRelativeUri(*resolutionScope, *documentUri);
|
||||||
*resolutionScope, *documentUri);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return *resolutionScope;
|
return *resolutionScope;
|
||||||
@ -248,8 +238,7 @@ private:
|
|||||||
if (itr == o.end()) {
|
if (itr == o.end()) {
|
||||||
return false;
|
return false;
|
||||||
} else if (!itr->second.asString(result)) {
|
} else if (!itr->second.asString(result)) {
|
||||||
throw std::invalid_argument(
|
throw std::invalid_argument("$ref property expected to contain string value.");
|
||||||
"$ref property expected to contain string value.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -258,7 +247,7 @@ private:
|
|||||||
/**
|
/**
|
||||||
* Sanitise an optional JSON Pointer, trimming trailing slashes
|
* Sanitise an optional JSON Pointer, trimming trailing slashes
|
||||||
*/
|
*/
|
||||||
std::string sanitiseJsonPointer(const opt::optional<std::string> input)
|
static std::string sanitiseJsonPointer(const opt::optional<std::string>& input)
|
||||||
{
|
{
|
||||||
if (input) {
|
if (input) {
|
||||||
// Trim trailing slash(es)
|
// Trim trailing slash(es)
|
||||||
@ -310,15 +299,14 @@ private:
|
|||||||
* usage of the schema cache during development, and is not expected
|
* usage of the schema cache during development, and is not expected
|
||||||
* to occur otherwise, even for malformed schemas.
|
* to occur otherwise, even for malformed schemas.
|
||||||
*/
|
*/
|
||||||
void updateSchemaCache(SchemaCache &schemaCache,
|
static void updateSchemaCache(SchemaCache &schemaCache,
|
||||||
const std::vector<std::string> &keysToCreate,
|
const std::vector<std::string> &keysToCreate,
|
||||||
const Subschema *schema)
|
const Subschema *schema)
|
||||||
{
|
{
|
||||||
for (const std::string &keyToCreate : keysToCreate) {
|
for (const std::string &keyToCreate : keysToCreate) {
|
||||||
const SchemaCache::value_type value(keyToCreate, schema);
|
const SchemaCache::value_type value(keyToCreate, schema);
|
||||||
if (!schemaCache.insert(value).second) {
|
if (!schemaCache.insert(value).second) {
|
||||||
throw std::logic_error(
|
throw std::logic_error("Key '" + keyToCreate + "' already in schema cache.");
|
||||||
"Key '" + keyToCreate + "' already in schema cache.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -375,16 +363,13 @@ private:
|
|||||||
|
|
||||||
// Construct a key that we can use to search the schema cache for
|
// Construct a key that we can use to search the schema cache for
|
||||||
// a schema corresponding to the current node
|
// a schema corresponding to the current node
|
||||||
const std::string schemaCacheKey =
|
const std::string schemaCacheKey = currentScope ? (*currentScope + nodePath) : nodePath;
|
||||||
currentScope ? (*currentScope + nodePath) : nodePath;
|
|
||||||
|
|
||||||
// Retrieve an existing schema from the cache if possible
|
// Retrieve an existing schema from the cache if possible
|
||||||
const Subschema *cachedPtr =
|
const Subschema *cachedPtr = querySchemaCache(schemaCache, schemaCacheKey);
|
||||||
querySchemaCache(schemaCache, schemaCacheKey);
|
|
||||||
|
|
||||||
// Create a new schema otherwise
|
// Create a new schema otherwise
|
||||||
const Subschema *subschema = cachedPtr ? cachedPtr :
|
const Subschema *subschema = cachedPtr ? cachedPtr : rootSchema.createSubschema();
|
||||||
rootSchema.createSubschema();
|
|
||||||
|
|
||||||
// Add cache entries for keys belonging to any $ref nodes that were
|
// Add cache entries for keys belonging to any $ref nodes that were
|
||||||
// visited before arriving at the current node
|
// visited before arriving at the current node
|
||||||
@ -404,8 +389,7 @@ private:
|
|||||||
|
|
||||||
// Returns a document URI if the reference points somewhere
|
// Returns a document URI if the reference points somewhere
|
||||||
// other than the current document
|
// other than the current document
|
||||||
const opt::optional<std::string> documentUri =
|
const opt::optional<std::string> documentUri = internal::json_reference::getJsonReferenceUri(jsonRef);
|
||||||
internal::json_reference::getJsonReferenceUri(jsonRef);
|
|
||||||
|
|
||||||
// Extract JSON Pointer from JSON Reference, with any trailing
|
// Extract JSON Pointer from JSON Reference, with any trailing
|
||||||
// slashes removed so that keys in the schema cache end
|
// slashes removed so that keys in the schema cache end
|
||||||
@ -417,12 +401,10 @@ private:
|
|||||||
// scope. An absolute document URI will take precedence when
|
// scope. An absolute document URI will take precedence when
|
||||||
// present, otherwise we need to resolve the URI relative to
|
// present, otherwise we need to resolve the URI relative to
|
||||||
// the current resolution scope
|
// the current resolution scope
|
||||||
const opt::optional<std::string> actualDocumentUri =
|
const opt::optional<std::string> actualDocumentUri = findAbsoluteDocumentUri(currentScope, documentUri);
|
||||||
findAbsoluteDocumentUri(currentScope, documentUri);
|
|
||||||
|
|
||||||
// Construct a key to search the schema cache for an existing schema
|
// Construct a key to search the schema cache for an existing schema
|
||||||
const std::string queryKey = actualDocumentUri ?
|
const std::string queryKey = actualDocumentUri ? (*actualDocumentUri + actualJsonPointer) : actualJsonPointer;
|
||||||
(*actualDocumentUri + actualJsonPointer) : actualJsonPointer;
|
|
||||||
|
|
||||||
// Check for the second termination condition (found a $ref node that
|
// Check for the second termination condition (found a $ref node that
|
||||||
// already has an entry in the schema cache)
|
// already has an entry in the schema cache)
|
||||||
@ -441,8 +423,7 @@ private:
|
|||||||
if (docCacheItr == docCache.end()) {
|
if (docCacheItr == docCache.end()) {
|
||||||
// Resolve reference against remote document
|
// Resolve reference against remote document
|
||||||
if (!fetchDoc) {
|
if (!fetchDoc) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Fetching of remote JSON References not enabled.");
|
||||||
"Fetching of remote JSON References not enabled.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a pointer to the remote document that was
|
// Returns a pointer to the remote document that was
|
||||||
@ -453,9 +434,7 @@ private:
|
|||||||
|
|
||||||
// Can't proceed without the remote document
|
// Can't proceed without the remote document
|
||||||
if (!newDoc) {
|
if (!newDoc) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Failed to fetch referenced schema document: " + *actualDocumentUri);
|
||||||
"Failed to fetch referenced schema document: " +
|
|
||||||
*actualDocumentUri);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef typename DocumentCache<AdapterType>::Type::value_type
|
typedef typename DocumentCache<AdapterType>::Type::value_type
|
||||||
@ -579,7 +558,7 @@ private:
|
|||||||
const AdapterType &rootNode,
|
const AdapterType &rootNode,
|
||||||
const AdapterType &node,
|
const AdapterType &node,
|
||||||
const Subschema &subschema,
|
const Subschema &subschema,
|
||||||
const opt::optional<std::string> currentScope,
|
const opt::optional<std::string>& currentScope,
|
||||||
const std::string &nodePath,
|
const std::string &nodePath,
|
||||||
const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
|
const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
|
||||||
const Subschema *parentSubschema,
|
const Subschema *parentSubschema,
|
||||||
@ -593,7 +572,7 @@ private:
|
|||||||
"appropriate Adapter implementation");
|
"appropriate Adapter implementation");
|
||||||
|
|
||||||
if (!node.isObject()) {
|
if (!node.isObject()) {
|
||||||
if (version == kDraft7 && node.maybeBool()) {
|
if (m_version == kDraft7 && node.maybeBool()) {
|
||||||
// Boolean schema
|
// Boolean schema
|
||||||
if (!node.asBool()) {
|
if (!node.asBool()) {
|
||||||
rootSchema.setAlwaysInvalid(&subschema, true);
|
rootSchema.setAlwaysInvalid(&subschema, true);
|
||||||
@ -603,7 +582,7 @@ private:
|
|||||||
std::string s;
|
std::string s;
|
||||||
s += "Expected node at ";
|
s += "Expected node at ";
|
||||||
s += nodePath;
|
s += nodePath;
|
||||||
if (version == kDraft7) {
|
if (m_version == kDraft7) {
|
||||||
s += " to contain schema object or boolean value; actual node type is: ";
|
s += " to contain schema object or boolean value; actual node type is: ";
|
||||||
} else {
|
} else {
|
||||||
s += " to contain schema object; actual node type is: ";
|
s += " to contain schema object; actual node type is: ";
|
||||||
@ -618,15 +597,13 @@ private:
|
|||||||
|
|
||||||
// Check for 'id' attribute and update current scope
|
// Check for 'id' attribute and update current scope
|
||||||
opt::optional<std::string> updatedScope;
|
opt::optional<std::string> updatedScope;
|
||||||
if ((itr = object.find("id")) != object.end() &&
|
if ((itr = object.find("id")) != object.end() && itr->second.maybeString()) {
|
||||||
itr->second.maybeString()) {
|
|
||||||
const std::string id = itr->second.asString();
|
const std::string id = itr->second.asString();
|
||||||
rootSchema.setSubschemaId(&subschema, itr->second.asString());
|
rootSchema.setSubschemaId(&subschema, itr->second.asString());
|
||||||
if (!currentScope || internal::uri::isUriAbsolute(id)) {
|
if (!currentScope || internal::uri::isUriAbsolute(id)) {
|
||||||
updatedScope = id;
|
updatedScope = id;
|
||||||
} else {
|
} else {
|
||||||
updatedScope = internal::uri::resolveRelativeUri(
|
updatedScope = internal::uri::resolveRelativeUri(*currentScope, id);
|
||||||
*currentScope, id);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
updatedScope = currentScope;
|
updatedScope = currentScope;
|
||||||
@ -679,7 +656,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((itr = object.find("divisibleBy")) != object.end()) {
|
if ((itr = object.find("divisibleBy")) != object.end()) {
|
||||||
if (version == kDraft3) {
|
if (m_version == kDraft3) {
|
||||||
if (itr->second.maybeInteger()) {
|
if (itr->second.maybeInteger()) {
|
||||||
rootSchema.addConstraintToSubschema(
|
rootSchema.addConstraintToSubschema(
|
||||||
makeMultipleOfIntConstraint(itr->second),
|
makeMultipleOfIntConstraint(itr->second),
|
||||||
@ -720,10 +697,8 @@ private:
|
|||||||
additionalItemsItr = object.find("additionalItems");
|
additionalItemsItr = object.find("additionalItems");
|
||||||
rootSchema.addConstraintToSubschema(
|
rootSchema.addConstraintToSubschema(
|
||||||
makeLinearItemsConstraint(rootSchema, rootNode,
|
makeLinearItemsConstraint(rootSchema, rootNode,
|
||||||
itemsItr != object.end() ?
|
itemsItr != object.end() ? &itemsItr->second : nullptr,
|
||||||
&itemsItr->second : nullptr,
|
additionalItemsItr != object.end() ? &additionalItemsItr->second : nullptr,
|
||||||
additionalItemsItr != object.end() ?
|
|
||||||
&additionalItemsItr->second : nullptr,
|
|
||||||
updatedScope, nodePath + "/items",
|
updatedScope, nodePath + "/items",
|
||||||
nodePath + "/additionalItems", fetchDoc,
|
nodePath + "/additionalItems", fetchDoc,
|
||||||
docCache, schemaCache),
|
docCache, schemaCache),
|
||||||
@ -738,7 +713,7 @@ private:
|
|||||||
const typename AdapterType::Object::const_iterator elseItr = object.find("else");
|
const typename AdapterType::Object::const_iterator elseItr = object.find("else");
|
||||||
|
|
||||||
if (object.end() != ifItr) {
|
if (object.end() != ifItr) {
|
||||||
if (version == kDraft7) {
|
if (m_version == kDraft7) {
|
||||||
rootSchema.addConstraintToSubschema(
|
rootSchema.addConstraintToSubschema(
|
||||||
makeConditionalConstraint(rootSchema, rootNode,
|
makeConditionalConstraint(rootSchema, rootNode,
|
||||||
ifItr->second,
|
ifItr->second,
|
||||||
@ -752,7 +727,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version == kDraft7) {
|
if (m_version == kDraft7) {
|
||||||
if ((itr = object.find("exclusiveMaximum")) != object.end()) {
|
if ((itr = object.find("exclusiveMaximum")) != object.end()) {
|
||||||
rootSchema.addConstraintToSubschema(
|
rootSchema.addConstraintToSubschema(
|
||||||
makeMaximumConstraintExclusive(itr->second),
|
makeMaximumConstraintExclusive(itr->second),
|
||||||
@ -773,13 +748,11 @@ private:
|
|||||||
&subschema);
|
&subschema);
|
||||||
} else {
|
} else {
|
||||||
rootSchema.addConstraintToSubschema(
|
rootSchema.addConstraintToSubschema(
|
||||||
makeMaximumConstraint(itr->second,
|
makeMaximumConstraint(itr->second, &exclusiveMaximumItr->second),
|
||||||
&exclusiveMaximumItr->second),
|
|
||||||
&subschema);
|
&subschema);
|
||||||
}
|
}
|
||||||
} else if (object.find("exclusiveMaximum") != object.end()) {
|
} else if (object.find("exclusiveMaximum") != object.end()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("'exclusiveMaximum' constraint only valid if a 'maximum' "
|
||||||
"'exclusiveMaximum' constraint only valid if a 'maximum' "
|
|
||||||
"constraint is also present");
|
"constraint is also present");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -798,11 +771,10 @@ private:
|
|||||||
makeMaxPropertiesConstraint(itr->second), &subschema);
|
makeMaxPropertiesConstraint(itr->second), &subschema);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version == kDraft7) {
|
if (m_version == kDraft7) {
|
||||||
if ((itr = object.find("exclusiveMinimum")) != object.end()) {
|
if ((itr = object.find("exclusiveMinimum")) != object.end()) {
|
||||||
rootSchema.addConstraintToSubschema(
|
rootSchema.addConstraintToSubschema(
|
||||||
makeMinimumConstraintExclusive(itr->second),
|
makeMinimumConstraintExclusive(itr->second), &subschema);
|
||||||
&subschema);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((itr = object.find("minimum")) != object.end()) {
|
if ((itr = object.find("minimum")) != object.end()) {
|
||||||
@ -811,22 +783,18 @@ private:
|
|||||||
&subschema);
|
&subschema);
|
||||||
}
|
}
|
||||||
} else if ((itr = object.find("minimum")) != object.end()) {
|
} else if ((itr = object.find("minimum")) != object.end()) {
|
||||||
typename AdapterType::Object::const_iterator exclusiveMinimumItr =
|
typename AdapterType::Object::const_iterator exclusiveMinimumItr = object.find("exclusiveMinimum");
|
||||||
object.find("exclusiveMinimum");
|
|
||||||
if (exclusiveMinimumItr == object.end()) {
|
if (exclusiveMinimumItr == object.end()) {
|
||||||
rootSchema.addConstraintToSubschema(
|
rootSchema.addConstraintToSubschema(
|
||||||
makeMinimumConstraint<AdapterType>(itr->second, nullptr),
|
makeMinimumConstraint<AdapterType>(itr->second, nullptr),
|
||||||
&subschema);
|
&subschema);
|
||||||
} else {
|
} else {
|
||||||
rootSchema.addConstraintToSubschema(
|
rootSchema.addConstraintToSubschema(
|
||||||
makeMinimumConstraint<AdapterType>(
|
makeMinimumConstraint<AdapterType>(itr->second, &exclusiveMinimumItr->second),
|
||||||
itr->second,
|
|
||||||
&exclusiveMinimumItr->second),
|
|
||||||
&subschema);
|
&subschema);
|
||||||
}
|
}
|
||||||
} else if (object.find("exclusiveMinimum") != object.end()) {
|
} else if (object.find("exclusiveMinimum") != object.end()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("'exclusiveMinimum' constraint only valid if a 'minimum' "
|
||||||
"'exclusiveMinimum' constraint only valid if a 'minimum' "
|
|
||||||
"constraint is also present");
|
"constraint is also present");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -846,9 +814,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((itr = object.find("multipleOf")) != object.end()) {
|
if ((itr = object.find("multipleOf")) != object.end()) {
|
||||||
if (version == kDraft3) {
|
if (m_version == kDraft3) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("'multipleOf' constraint not available in draft 3");
|
||||||
"'multipleOf' constraint not available in draft 3");
|
|
||||||
} else if (itr->second.maybeInteger()) {
|
} else if (itr->second.maybeInteger()) {
|
||||||
rootSchema.addConstraintToSubschema(
|
rootSchema.addConstraintToSubschema(
|
||||||
makeMultipleOfIntConstraint(itr->second),
|
makeMultipleOfIntConstraint(itr->second),
|
||||||
@ -858,23 +825,20 @@ private:
|
|||||||
makeMultipleOfDoubleConstraint(itr->second),
|
makeMultipleOfDoubleConstraint(itr->second),
|
||||||
&subschema);
|
&subschema);
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error("Expected an numeric value for "
|
throw std::runtime_error("Expected an numeric value for 'divisibleBy' constraint.");
|
||||||
" 'divisibleBy' constraint.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((itr = object.find("not")) != object.end()) {
|
if ((itr = object.find("not")) != object.end()) {
|
||||||
rootSchema.addConstraintToSubschema(
|
rootSchema.addConstraintToSubschema(
|
||||||
makeNotConstraint(rootSchema, rootNode, itr->second,
|
makeNotConstraint(rootSchema, rootNode, itr->second, updatedScope, nodePath + "/not", fetchDoc,
|
||||||
updatedScope, nodePath + "/not", fetchDoc, docCache,
|
docCache, schemaCache),
|
||||||
schemaCache),
|
|
||||||
&subschema);
|
&subschema);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((itr = object.find("oneOf")) != object.end()) {
|
if ((itr = object.find("oneOf")) != object.end()) {
|
||||||
rootSchema.addConstraintToSubschema(
|
rootSchema.addConstraintToSubschema(
|
||||||
makeOneOfConstraint(rootSchema, rootNode, itr->second,
|
makeOneOfConstraint(rootSchema, rootNode, itr->second, updatedScope, nodePath + "/oneOf", fetchDoc,
|
||||||
updatedScope, nodePath + "/oneOf", fetchDoc,
|
|
||||||
docCache, schemaCache),
|
docCache, schemaCache),
|
||||||
&subschema);
|
&subschema);
|
||||||
}
|
}
|
||||||
@ -896,12 +860,9 @@ private:
|
|||||||
object.end() != additionalPropertiesItr) {
|
object.end() != additionalPropertiesItr) {
|
||||||
rootSchema.addConstraintToSubschema(
|
rootSchema.addConstraintToSubschema(
|
||||||
makePropertiesConstraint(rootSchema, rootNode,
|
makePropertiesConstraint(rootSchema, rootNode,
|
||||||
propertiesItr != object.end() ?
|
propertiesItr != object.end() ? &propertiesItr->second : nullptr,
|
||||||
&propertiesItr->second : nullptr,
|
patternPropertiesItr != object.end() ? &patternPropertiesItr->second : nullptr,
|
||||||
patternPropertiesItr != object.end() ?
|
additionalPropertiesItr != object.end() ? &additionalPropertiesItr->second : nullptr,
|
||||||
&patternPropertiesItr->second : nullptr,
|
|
||||||
additionalPropertiesItr != object.end() ?
|
|
||||||
&additionalPropertiesItr->second : nullptr,
|
|
||||||
updatedScope, nodePath + "/properties",
|
updatedScope, nodePath + "/properties",
|
||||||
nodePath + "/patternProperties",
|
nodePath + "/patternProperties",
|
||||||
nodePath + "/additionalProperties",
|
nodePath + "/additionalProperties",
|
||||||
@ -911,7 +872,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((itr = object.find("propertyNames")) != object.end()) {
|
if ((itr = object.find("propertyNames")) != object.end()) {
|
||||||
if (version == kDraft7) {
|
if (m_version == kDraft7) {
|
||||||
rootSchema.addConstraintToSubschema(
|
rootSchema.addConstraintToSubschema(
|
||||||
makePropertyNamesConstraint(rootSchema, rootNode, itr->second, updatedScope,
|
makePropertyNamesConstraint(rootSchema, rootNode, itr->second, updatedScope,
|
||||||
nodePath, fetchDoc, docCache, schemaCache),
|
nodePath, fetchDoc, docCache, schemaCache),
|
||||||
@ -922,60 +883,49 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((itr = object.find("required")) != object.end()) {
|
if ((itr = object.find("required")) != object.end()) {
|
||||||
if (version == kDraft3) {
|
if (m_version == kDraft3) {
|
||||||
if (parentSubschema && ownName) {
|
if (parentSubschema && ownName) {
|
||||||
opt::optional<constraints::RequiredConstraint>
|
opt::optional<constraints::RequiredConstraint> constraint =
|
||||||
constraint = makeRequiredConstraintForSelf(
|
makeRequiredConstraintForSelf(itr->second, *ownName);
|
||||||
itr->second, *ownName);
|
|
||||||
if (constraint) {
|
if (constraint) {
|
||||||
rootSchema.addConstraintToSubschema(*constraint,
|
rootSchema.addConstraintToSubschema(*constraint, parentSubschema);
|
||||||
parentSubschema);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("'required' constraint not valid here");
|
||||||
"'required' constraint not valid here");
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rootSchema.addConstraintToSubschema(
|
rootSchema.addConstraintToSubschema(makeRequiredConstraint(itr->second), &subschema);
|
||||||
makeRequiredConstraint(itr->second), &subschema);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((itr = object.find("title")) != object.end()) {
|
if ((itr = object.find("title")) != object.end()) {
|
||||||
if (itr->second.maybeString()) {
|
if (itr->second.maybeString()) {
|
||||||
rootSchema.setSubschemaTitle(&subschema,
|
rootSchema.setSubschemaTitle(&subschema, itr->second.asString());
|
||||||
itr->second.asString());
|
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("'title' attribute should have a string value");
|
||||||
"'title' attribute should have a string value");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((itr = object.find("type")) != object.end()) {
|
if ((itr = object.find("type")) != object.end()) {
|
||||||
rootSchema.addConstraintToSubschema(
|
rootSchema.addConstraintToSubschema(
|
||||||
makeTypeConstraint(rootSchema, rootNode, itr->second,
|
makeTypeConstraint(rootSchema, rootNode, itr->second, updatedScope, nodePath + "/type", fetchDoc,
|
||||||
updatedScope, nodePath + "/type", fetchDoc,
|
|
||||||
docCache, schemaCache),
|
docCache, schemaCache),
|
||||||
&subschema);
|
&subschema);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((itr = object.find("uniqueItems")) != object.end()) {
|
if ((itr = object.find("uniqueItems")) != object.end()) {
|
||||||
opt::optional<constraints::UniqueItemsConstraint> constraint =
|
opt::optional<constraints::UniqueItemsConstraint> constraint = makeUniqueItemsConstraint(itr->second);
|
||||||
makeUniqueItemsConstraint(itr->second);
|
|
||||||
if (constraint) {
|
if (constraint) {
|
||||||
rootSchema.addConstraintToSubschema(*constraint, &subschema);
|
rootSchema.addConstraintToSubschema(*constraint, &subschema);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ConstraintBuilders::const_iterator
|
for (const auto & constraintBuilder : constraintBuilders) {
|
||||||
builderItr = constraintBuilders.begin();
|
if ((itr = object.find(constraintBuilder.first)) != object.end()) {
|
||||||
builderItr != constraintBuilders.end(); ++builderItr) {
|
|
||||||
if ((itr = object.find(builderItr->first)) != object.end()) {
|
|
||||||
constraints::Constraint *constraint = nullptr;
|
constraints::Constraint *constraint = nullptr;
|
||||||
try {
|
try {
|
||||||
constraint = builderItr->second->make(itr->second);
|
constraint = constraintBuilder.second->make(itr->second);
|
||||||
rootSchema.addConstraintToSubschema(*constraint,
|
rootSchema.addConstraintToSubschema(*constraint, &subschema);
|
||||||
&subschema);
|
|
||||||
delete constraint;
|
delete constraint;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
delete constraint;
|
delete constraint;
|
||||||
@ -1026,16 +976,14 @@ private:
|
|||||||
{
|
{
|
||||||
std::string jsonRef;
|
std::string jsonRef;
|
||||||
if (!extractJsonReference(node, jsonRef)) {
|
if (!extractJsonReference(node, jsonRef)) {
|
||||||
populateSchema(rootSchema, rootNode, node, subschema, currentScope,
|
populateSchema(rootSchema, rootNode, node, subschema, currentScope, nodePath, fetchDoc, parentSchema,
|
||||||
nodePath, fetchDoc, parentSchema, ownName, docCache,
|
ownName, docCache, schemaCache);
|
||||||
schemaCache);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a document URI if the reference points somewhere
|
// Returns a document URI if the reference points somewhere
|
||||||
// other than the current document
|
// other than the current document
|
||||||
const opt::optional<std::string> documentUri =
|
const opt::optional<std::string> documentUri = internal::json_reference::getJsonReferenceUri(jsonRef);
|
||||||
internal::json_reference::getJsonReferenceUri(jsonRef);
|
|
||||||
|
|
||||||
// Extract JSON Pointer from JSON Reference
|
// Extract JSON Pointer from JSON Reference
|
||||||
const std::string actualJsonPointer = sanitiseJsonPointer(
|
const std::string actualJsonPointer = sanitiseJsonPointer(
|
||||||
@ -1044,48 +992,37 @@ private:
|
|||||||
if (documentUri && internal::uri::isUriAbsolute(*documentUri)) {
|
if (documentUri && internal::uri::isUriAbsolute(*documentUri)) {
|
||||||
// Resolve reference against remote document
|
// Resolve reference against remote document
|
||||||
if (!fetchDoc) {
|
if (!fetchDoc) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Fetching of remote JSON References not enabled.");
|
||||||
"Fetching of remote JSON References not enabled.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const typename DocumentCache<AdapterType>::DocumentType *newDoc =
|
const typename DocumentCache<AdapterType>::DocumentType *newDoc = fetchDoc(*documentUri);
|
||||||
fetchDoc(*documentUri);
|
|
||||||
|
|
||||||
// Can't proceed without the remote document
|
// Can't proceed without the remote document
|
||||||
if (!newDoc) {
|
if (!newDoc) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Failed to fetch referenced schema document: " + *documentUri);
|
||||||
"Failed to fetch referenced schema document: " +
|
|
||||||
*documentUri);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to document cache
|
// Add to document cache
|
||||||
typedef typename DocumentCache<AdapterType>::Type::value_type
|
typedef typename DocumentCache<AdapterType>::Type::value_type DocCacheValueType;
|
||||||
DocCacheValueType;
|
|
||||||
|
|
||||||
docCache.insert(DocCacheValueType(*documentUri, newDoc));
|
docCache.insert(DocCacheValueType(*documentUri, newDoc));
|
||||||
|
|
||||||
const AdapterType newRootNode(*newDoc);
|
const AdapterType newRootNode(*newDoc);
|
||||||
|
|
||||||
const AdapterType &referencedAdapter =
|
const AdapterType &referencedAdapter =
|
||||||
internal::json_pointer::resolveJsonPointer(
|
internal::json_pointer::resolveJsonPointer(newRootNode, actualJsonPointer);
|
||||||
newRootNode, actualJsonPointer);
|
|
||||||
|
|
||||||
// TODO: Need to detect degenerate circular references
|
// TODO: Need to detect degenerate circular references
|
||||||
resolveThenPopulateSchema(rootSchema, newRootNode,
|
resolveThenPopulateSchema(rootSchema, newRootNode, referencedAdapter, subschema, {}, actualJsonPointer,
|
||||||
referencedAdapter, subschema, opt::optional<std::string>(),
|
fetchDoc, parentSchema, ownName, docCache, schemaCache);
|
||||||
actualJsonPointer, fetchDoc, parentSchema, ownName,
|
|
||||||
docCache, schemaCache);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const AdapterType &referencedAdapter =
|
const AdapterType &referencedAdapter =
|
||||||
internal::json_pointer::resolveJsonPointer(
|
internal::json_pointer::resolveJsonPointer(rootNode, actualJsonPointer);
|
||||||
rootNode, actualJsonPointer);
|
|
||||||
|
|
||||||
// TODO: Need to detect degenerate circular references
|
// TODO: Need to detect degenerate circular references
|
||||||
resolveThenPopulateSchema(rootSchema, rootNode, referencedAdapter,
|
resolveThenPopulateSchema(rootSchema, rootNode, referencedAdapter, subschema, {}, actualJsonPointer,
|
||||||
subschema, opt::optional<std::string>(),
|
fetchDoc, parentSchema, ownName, docCache, schemaCache);
|
||||||
actualJsonPointer, fetchDoc,
|
|
||||||
parentSchema, ownName, docCache, schemaCache);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1119,15 +1056,14 @@ private:
|
|||||||
SchemaCache &schemaCache)
|
SchemaCache &schemaCache)
|
||||||
{
|
{
|
||||||
if (!node.maybeArray()) {
|
if (!node.maybeArray()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected array value for 'allOf' constraint.");
|
||||||
"Expected array value for 'allOf' constraint.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constraints::AllOfConstraint constraint;
|
constraints::AllOfConstraint constraint;
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (const AdapterType schemaNode : node.asArray()) {
|
for (const AdapterType schemaNode : node.asArray()) {
|
||||||
if (schemaNode.maybeObject() || (version == kDraft7 && schemaNode.isBool())) {
|
if (schemaNode.maybeObject() || (m_version == kDraft7 && schemaNode.isBool())) {
|
||||||
const std::string childPath = nodePath + "/" + std::to_string(index);
|
const std::string childPath = nodePath + "/" + std::to_string(index);
|
||||||
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
||||||
rootSchema, rootNode, schemaNode, currentScope,
|
rootSchema, rootNode, schemaNode, currentScope,
|
||||||
@ -1135,8 +1071,7 @@ private:
|
|||||||
constraint.addSubschema(subschema);
|
constraint.addSubschema(subschema);
|
||||||
index++;
|
index++;
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected element to be a valid schema in 'allOf' constraint.");
|
||||||
"Expected element to be a valid schema in 'allOf' constraint.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1173,15 +1108,14 @@ private:
|
|||||||
SchemaCache &schemaCache)
|
SchemaCache &schemaCache)
|
||||||
{
|
{
|
||||||
if (!node.maybeArray()) {
|
if (!node.maybeArray()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected array value for 'anyOf' constraint.");
|
||||||
"Expected array value for 'anyOf' constraint.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constraints::AnyOfConstraint constraint;
|
constraints::AnyOfConstraint constraint;
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (const AdapterType schemaNode : node.asArray()) {
|
for (const AdapterType schemaNode : node.asArray()) {
|
||||||
if (schemaNode.maybeObject() || (version == kDraft7 && schemaNode.isBool())) {
|
if (schemaNode.maybeObject() || (m_version == kDraft7 && schemaNode.isBool())) {
|
||||||
const std::string childPath = nodePath + "/" + std::to_string(index);
|
const std::string childPath = nodePath + "/" + std::to_string(index);
|
||||||
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
||||||
rootSchema, rootNode, schemaNode, currentScope,
|
rootSchema, rootNode, schemaNode, currentScope,
|
||||||
@ -1189,8 +1123,7 @@ private:
|
|||||||
constraint.addSubschema(subschema);
|
constraint.addSubschema(subschema);
|
||||||
index++;
|
index++;
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected array element to be a valid schema in 'anyOf' constraint.");
|
||||||
"Expected array element to be a valid schema in 'anyOf' constraint.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1316,7 +1249,7 @@ private:
|
|||||||
{
|
{
|
||||||
constraints::ContainsConstraint constraint;
|
constraints::ContainsConstraint constraint;
|
||||||
|
|
||||||
if (contains.isObject() || (version == kDraft7 && contains.maybeBool())) {
|
if (contains.isObject() || (m_version == kDraft7 && contains.maybeBool())) {
|
||||||
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
||||||
rootSchema, rootNode, contains, currentScope, containsPath,
|
rootSchema, rootNode, contains, currentScope, containsPath,
|
||||||
fetchDoc, nullptr, nullptr, docCache, schemaCache);
|
fetchDoc, nullptr, nullptr, docCache, schemaCache);
|
||||||
@ -1329,8 +1262,7 @@ private:
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
// All other formats will result in an exception being thrown.
|
// All other formats will result in an exception being thrown.
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected valid schema for 'contains' constraint.");
|
||||||
"Expected valid schema for 'contains' constraint.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return constraint;
|
return constraint;
|
||||||
@ -1420,7 +1352,7 @@ private:
|
|||||||
// exercised the flexibility by loosely-typed Adapter types. If the
|
// exercised the flexibility by loosely-typed Adapter types. If the
|
||||||
// value of the dependency mapping is an object, then we'll try to
|
// value of the dependency mapping is an object, then we'll try to
|
||||||
// process it as a dependent schema.
|
// process it as a dependent schema.
|
||||||
} else if (member.second.isObject() || (version == kDraft7 && member.second.maybeBool())) {
|
} else if (member.second.isObject() || (m_version == kDraft7 && member.second.maybeBool())) {
|
||||||
// Parse dependent subschema
|
// Parse dependent subschema
|
||||||
const Subschema *childSubschema =
|
const Subschema *childSubschema =
|
||||||
makeOrReuseSchema<AdapterType>(rootSchema, rootNode,
|
makeOrReuseSchema<AdapterType>(rootSchema, rootNode,
|
||||||
@ -1432,7 +1364,7 @@ private:
|
|||||||
// If we're supposed to be parsing a Draft3 schema, then the value
|
// If we're supposed to be parsing a Draft3 schema, then the value
|
||||||
// of the dependency mapping can also be a string containing the
|
// of the dependency mapping can also be a string containing the
|
||||||
// name of a single dependency.
|
// name of a single dependency.
|
||||||
} else if (version == kDraft3 && member.second.isString()) {
|
} else if (m_version == kDraft3 && member.second.isString()) {
|
||||||
dependenciesConstraint.addPropertyDependency(member.first,
|
dependenciesConstraint.addPropertyDependency(member.first,
|
||||||
member.second.getString());
|
member.second.getString());
|
||||||
|
|
||||||
@ -1522,8 +1454,7 @@ private:
|
|||||||
// and is set to true, then additional array items do not need
|
// and is set to true, then additional array items do not need
|
||||||
// to satisfy any constraints.
|
// to satisfy any constraints.
|
||||||
if (additionalItems->asBool()) {
|
if (additionalItems->asBool()) {
|
||||||
constraint.setAdditionalItemsSubschema(
|
constraint.setAdditionalItemsSubschema(rootSchema.emptySubschema());
|
||||||
rootSchema.emptySubschema());
|
|
||||||
}
|
}
|
||||||
} else if (additionalItems->maybeObject()) {
|
} else if (additionalItems->maybeObject()) {
|
||||||
// If the value of the additionalItems property is an object,
|
// If the value of the additionalItems property is an object,
|
||||||
@ -1537,8 +1468,7 @@ private:
|
|||||||
} else {
|
} else {
|
||||||
// Any other format for the additionalItems property will result
|
// Any other format for the additionalItems property will result
|
||||||
// in an exception being thrown.
|
// in an exception being thrown.
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected bool or object value for 'additionalItems'");
|
||||||
"Expected bool or object value for 'additionalItems'");
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// The default value for the additionalItems property is an empty
|
// The default value for the additionalItems property is an empty
|
||||||
@ -1567,9 +1497,7 @@ private:
|
|||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected array value for non-singular 'items' constraint.");
|
||||||
"Expected array value for non-singular 'items' "
|
|
||||||
"constraint.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1622,7 +1550,7 @@ private:
|
|||||||
// array is provided, or a single Schema object, in an object value is
|
// array is provided, or a single Schema object, in an object value is
|
||||||
// provided. If the items constraint is not provided, then array items
|
// provided. If the items constraint is not provided, then array items
|
||||||
// will be validated against the additionalItems schema.
|
// will be validated against the additionalItems schema.
|
||||||
if (items.isObject() || (version == kDraft7 && items.maybeBool())) {
|
if (items.isObject() || (m_version == kDraft7 && items.maybeBool())) {
|
||||||
// If the items constraint contains an object value, then it
|
// If the items constraint contains an object value, then it
|
||||||
// should contain a Schema that will be used to validate all
|
// should contain a Schema that will be used to validate all
|
||||||
// items in a target array. Any schema defined by the
|
// items in a target array. Any schema defined by the
|
||||||
@ -1639,8 +1567,7 @@ private:
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
// All other formats will result in an exception being thrown.
|
// All other formats will result in an exception being thrown.
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected valid schema for singular 'items' constraint.");
|
||||||
"Expected valid schema for singular 'items' constraint.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return constraint;
|
return constraint;
|
||||||
@ -1669,8 +1596,7 @@ private:
|
|||||||
const AdapterType *exclusiveMaximum)
|
const AdapterType *exclusiveMaximum)
|
||||||
{
|
{
|
||||||
if (!node.maybeDouble()) {
|
if (!node.maybeDouble()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected numeric value for maximum constraint.");
|
||||||
"Expected numeric value for maximum constraint.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constraints::MaximumConstraint constraint;
|
constraints::MaximumConstraint constraint;
|
||||||
@ -1678,9 +1604,7 @@ private:
|
|||||||
|
|
||||||
if (exclusiveMaximum) {
|
if (exclusiveMaximum) {
|
||||||
if (!exclusiveMaximum->maybeBool()) {
|
if (!exclusiveMaximum->maybeBool()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected boolean value for exclusiveMaximum constraint.");
|
||||||
"Expected boolean value for exclusiveMaximum "
|
|
||||||
"constraint.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constraint.setExclusiveMaximum(exclusiveMaximum->asBool());
|
constraint.setExclusiveMaximum(exclusiveMaximum->asBool());
|
||||||
@ -1733,9 +1657,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected non-negative integer value for 'maxItems' constraint.");
|
||||||
"Expected non-negative integer value for 'maxItems' "
|
|
||||||
"constraint.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1759,9 +1681,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected a non-negative integer value for 'maxLength' constraint.");
|
||||||
"Expected a non-negative integer value for 'maxLength' "
|
|
||||||
"constraint.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1787,9 +1707,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected a non-negative integer for 'maxProperties' constraint.");
|
||||||
"Expected a non-negative integer for 'maxProperties' "
|
|
||||||
"constraint.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1810,8 +1728,7 @@ private:
|
|||||||
const AdapterType *exclusiveMinimum)
|
const AdapterType *exclusiveMinimum)
|
||||||
{
|
{
|
||||||
if (!node.maybeDouble()) {
|
if (!node.maybeDouble()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected numeric value for minimum constraint.");
|
||||||
"Expected numeric value for minimum constraint.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constraints::MinimumConstraint constraint;
|
constraints::MinimumConstraint constraint;
|
||||||
@ -1819,9 +1736,7 @@ private:
|
|||||||
|
|
||||||
if (exclusiveMinimum) {
|
if (exclusiveMinimum) {
|
||||||
if (!exclusiveMinimum->maybeBool()) {
|
if (!exclusiveMinimum->maybeBool()) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected boolean value for 'exclusiveMinimum' constraint.");
|
||||||
"Expected boolean value for 'exclusiveMinimum' "
|
|
||||||
"constraint.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constraint.setExclusiveMinimum(exclusiveMinimum->asBool());
|
constraint.setExclusiveMinimum(exclusiveMinimum->asBool());
|
||||||
@ -1862,8 +1777,7 @@ private:
|
|||||||
* @return pointer to a new MinItemsConstraint that belongs to the caller
|
* @return pointer to a new MinItemsConstraint that belongs to the caller
|
||||||
*/
|
*/
|
||||||
template<typename AdapterType>
|
template<typename AdapterType>
|
||||||
constraints::MinItemsConstraint makeMinItemsConstraint(
|
constraints::MinItemsConstraint makeMinItemsConstraint(const AdapterType &node)
|
||||||
const AdapterType &node)
|
|
||||||
{
|
{
|
||||||
if (node.maybeInteger()) {
|
if (node.maybeInteger()) {
|
||||||
const int64_t value = node.asInteger();
|
const int64_t value = node.asInteger();
|
||||||
@ -1874,9 +1788,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected a non-negative integer value for 'minItems' constraint.");
|
||||||
"Expected a non-negative integer value for 'minItems' "
|
|
||||||
"constraint.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1888,8 +1800,7 @@ private:
|
|||||||
* @return pointer to a new MinLengthConstraint that belongs to the caller
|
* @return pointer to a new MinLengthConstraint that belongs to the caller
|
||||||
*/
|
*/
|
||||||
template<typename AdapterType>
|
template<typename AdapterType>
|
||||||
constraints::MinLengthConstraint makeMinLengthConstraint(
|
constraints::MinLengthConstraint makeMinLengthConstraint(const AdapterType &node)
|
||||||
const AdapterType &node)
|
|
||||||
{
|
{
|
||||||
if (node.maybeInteger()) {
|
if (node.maybeInteger()) {
|
||||||
const int64_t value = node.asInteger();
|
const int64_t value = node.asInteger();
|
||||||
@ -1900,9 +1811,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected a non-negative integer value for 'minLength' constraint.");
|
||||||
"Expected a non-negative integer value for 'minLength' "
|
|
||||||
"constraint.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1917,8 +1826,7 @@ private:
|
|||||||
* caller
|
* caller
|
||||||
*/
|
*/
|
||||||
template<typename AdapterType>
|
template<typename AdapterType>
|
||||||
constraints::MinPropertiesConstraint makeMinPropertiesConstraint(
|
constraints::MinPropertiesConstraint makeMinPropertiesConstraint(const AdapterType &node)
|
||||||
const AdapterType &node)
|
|
||||||
{
|
{
|
||||||
if (node.maybeInteger()) {
|
if (node.maybeInteger()) {
|
||||||
int64_t value = node.asInteger();
|
int64_t value = node.asInteger();
|
||||||
@ -1929,9 +1837,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected a non-negative integer for 'minProperties' constraint.");
|
||||||
"Expected a non-negative integer for 'minProperties' "
|
|
||||||
"constraint.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1943,8 +1849,7 @@ private:
|
|||||||
* @return a MultipleOfConstraint
|
* @return a MultipleOfConstraint
|
||||||
*/
|
*/
|
||||||
template<typename AdapterType>
|
template<typename AdapterType>
|
||||||
constraints::MultipleOfDoubleConstraint makeMultipleOfDoubleConstraint(
|
constraints::MultipleOfDoubleConstraint makeMultipleOfDoubleConstraint(const AdapterType &node)
|
||||||
const AdapterType &node)
|
|
||||||
{
|
{
|
||||||
constraints::MultipleOfDoubleConstraint constraint;
|
constraints::MultipleOfDoubleConstraint constraint;
|
||||||
constraint.setDivisor(node.asDouble());
|
constraint.setDivisor(node.asDouble());
|
||||||
@ -1960,8 +1865,7 @@ private:
|
|||||||
* @return a MultipleOfIntConstraint
|
* @return a MultipleOfIntConstraint
|
||||||
*/
|
*/
|
||||||
template<typename AdapterType>
|
template<typename AdapterType>
|
||||||
constraints::MultipleOfIntConstraint makeMultipleOfIntConstraint(
|
constraints::MultipleOfIntConstraint makeMultipleOfIntConstraint(const AdapterType &node)
|
||||||
const AdapterType &node)
|
|
||||||
{
|
{
|
||||||
constraints::MultipleOfIntConstraint constraint;
|
constraints::MultipleOfIntConstraint constraint;
|
||||||
constraint.setDivisor(node.asInteger());
|
constraint.setDivisor(node.asInteger());
|
||||||
@ -1996,7 +1900,7 @@ private:
|
|||||||
typename DocumentCache<AdapterType>::Type &docCache,
|
typename DocumentCache<AdapterType>::Type &docCache,
|
||||||
SchemaCache &schemaCache)
|
SchemaCache &schemaCache)
|
||||||
{
|
{
|
||||||
if (node.maybeObject() || (version == kDraft7 && node.maybeBool())) {
|
if (node.maybeObject() || (m_version == kDraft7 && node.maybeBool())) {
|
||||||
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
||||||
rootSchema, rootNode, node, currentScope, nodePath,
|
rootSchema, rootNode, node, currentScope, nodePath,
|
||||||
fetchDoc, nullptr, nullptr, docCache, schemaCache);
|
fetchDoc, nullptr, nullptr, docCache, schemaCache);
|
||||||
@ -2040,8 +1944,7 @@ private:
|
|||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (const AdapterType schemaNode : node.getArray()) {
|
for (const AdapterType schemaNode : node.getArray()) {
|
||||||
const std::string childPath = nodePath + "/" +
|
const std::string childPath = nodePath + "/" + std::to_string(index);
|
||||||
std::to_string(index);
|
|
||||||
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
||||||
rootSchema, rootNode, schemaNode, currentScope, childPath,
|
rootSchema, rootNode, schemaNode, currentScope, childPath,
|
||||||
fetchDoc, nullptr, nullptr, docCache, schemaCache);
|
fetchDoc, nullptr, nullptr, docCache, schemaCache);
|
||||||
@ -2166,8 +2069,7 @@ private:
|
|||||||
// If it has a boolean value that is 'true', then an empty
|
// If it has a boolean value that is 'true', then an empty
|
||||||
// schema should be used.
|
// schema should be used.
|
||||||
if (additionalProperties->asBool()) {
|
if (additionalProperties->asBool()) {
|
||||||
constraint.setAdditionalPropertiesSubschema(
|
constraint.setAdditionalPropertiesSubschema(rootSchema.emptySubschema());
|
||||||
rootSchema.emptySubschema());
|
|
||||||
}
|
}
|
||||||
} else if (additionalProperties->isObject()) {
|
} else if (additionalProperties->isObject()) {
|
||||||
// If additionalProperties is an object, it should be used as
|
// If additionalProperties is an object, it should be used as
|
||||||
@ -2179,14 +2081,12 @@ private:
|
|||||||
constraint.setAdditionalPropertiesSubschema(subschema);
|
constraint.setAdditionalPropertiesSubschema(subschema);
|
||||||
} else {
|
} else {
|
||||||
// All other types are invalid
|
// All other types are invalid
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Invalid type for 'additionalProperties' constraint.");
|
||||||
"Invalid type for 'additionalProperties' constraint.");
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If an additionalProperties constraint is not provided, then the
|
// If an additionalProperties constraint is not provided, then the
|
||||||
// default value is an empty schema.
|
// default value is an empty schema.
|
||||||
constraint.setAdditionalPropertiesSubschema(
|
constraint.setAdditionalPropertiesSubschema(rootSchema.emptySubschema());
|
||||||
rootSchema.emptySubschema());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return constraint;
|
return constraint;
|
||||||
@ -2203,10 +2103,8 @@ private:
|
|||||||
typename DocumentCache<AdapterType>::Type &docCache,
|
typename DocumentCache<AdapterType>::Type &docCache,
|
||||||
SchemaCache &schemaCache)
|
SchemaCache &schemaCache)
|
||||||
{
|
{
|
||||||
const Subschema *subschema = makeOrReuseSchema<AdapterType>(rootSchema, rootNode,
|
const Subschema *subschema = makeOrReuseSchema<AdapterType>(rootSchema, rootNode, currentNode, currentScope,
|
||||||
currentNode, currentScope, nodePath, fetchDoc, nullptr, nullptr, docCache,
|
nodePath, fetchDoc, nullptr, nullptr, docCache, schemaCache);
|
||||||
schemaCache);
|
|
||||||
|
|
||||||
constraints::PropertyNamesConstraint constraint;
|
constraints::PropertyNamesConstraint constraint;
|
||||||
constraint.setSubschema(subschema);
|
constraint.setSubschema(subschema);
|
||||||
return constraint;
|
return constraint;
|
||||||
@ -2261,8 +2159,7 @@ private:
|
|||||||
|
|
||||||
for (const AdapterType v : node.getArray()) {
|
for (const AdapterType v : node.getArray()) {
|
||||||
if (!v.maybeString()) {
|
if (!v.maybeString()) {
|
||||||
throw std::runtime_error("Expected required property name to "
|
throw std::runtime_error("Expected required property name to be a string value");
|
||||||
"be a string value");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constraint.addRequiredProperty(v.getString());
|
constraint.addRequiredProperty(v.getString());
|
||||||
@ -2305,9 +2202,8 @@ private:
|
|||||||
|
|
||||||
if (node.maybeString()) {
|
if (node.maybeString()) {
|
||||||
const TypeConstraint::JsonType type = TypeConstraint::jsonTypeFromString(node.getString());
|
const TypeConstraint::JsonType type = TypeConstraint::jsonTypeFromString(node.getString());
|
||||||
if (type == TypeConstraint::kAny && version == kDraft4) {
|
if (type == TypeConstraint::kAny && m_version == kDraft4) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("'any' type is not supported in version 4 schemas.");
|
||||||
"'any' type is not supported in version 4 schemas.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constraint.addNamedType(type);
|
constraint.addNamedType(type);
|
||||||
@ -2316,22 +2212,17 @@ private:
|
|||||||
int index = 0;
|
int index = 0;
|
||||||
for (const AdapterType v : node.getArray()) {
|
for (const AdapterType v : node.getArray()) {
|
||||||
if (v.maybeString()) {
|
if (v.maybeString()) {
|
||||||
const TypeConstraint::JsonType type =
|
const TypeConstraint::JsonType type = TypeConstraint::jsonTypeFromString(v.getString());
|
||||||
TypeConstraint::jsonTypeFromString(v.getString());
|
if (type == TypeConstraint::kAny && m_version == kDraft4) {
|
||||||
|
throw std::runtime_error("'any' type is not supported in version 4 schemas.");
|
||||||
if (type == TypeConstraint::kAny && version == kDraft4) {
|
|
||||||
throw std::runtime_error(
|
|
||||||
"'any' type is not supported in version 4 "
|
|
||||||
"schemas.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constraint.addNamedType(type);
|
constraint.addNamedType(type);
|
||||||
|
|
||||||
} else if (v.maybeObject() && version == kDraft3) {
|
} else if (v.maybeObject() && m_version == kDraft3) {
|
||||||
const std::string childPath = nodePath + "/" + std::to_string(index);
|
const std::string childPath = nodePath + "/" + std::to_string(index);
|
||||||
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
const Subschema *subschema = makeOrReuseSchema<AdapterType>(rootSchema, rootNode, v, currentScope,
|
||||||
rootSchema, rootNode, v, currentScope, childPath,
|
childPath, fetchDoc, nullptr, nullptr, docCache, schemaCache);
|
||||||
fetchDoc, nullptr, nullptr, docCache, schemaCache);
|
|
||||||
constraint.addSchemaType(subschema);
|
constraint.addSchemaType(subschema);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -2341,10 +2232,9 @@ private:
|
|||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (node.maybeObject() && version == kDraft3) {
|
} else if (node.maybeObject() && m_version == kDraft3) {
|
||||||
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
const Subschema *subschema = makeOrReuseSchema<AdapterType>(rootSchema, rootNode, node, currentScope,
|
||||||
rootSchema, rootNode, node, currentScope, nodePath,
|
nodePath, fetchDoc, nullptr, nullptr, docCache, schemaCache);
|
||||||
fetchDoc, nullptr, nullptr, docCache, schemaCache);
|
|
||||||
constraint.addSchemaType(subschema);
|
constraint.addSchemaType(subschema);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -2376,10 +2266,13 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Expected boolean value for 'uniqueItems' constraint.");
|
||||||
"Expected boolean value for 'uniqueItems' constraint.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// Version of JSON Schema that should be expected when parsing
|
||||||
|
const Version m_version;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace valijson
|
} // namespace valijson
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace valijson {
|
namespace valijson {
|
||||||
@ -25,21 +26,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
struct Error
|
struct Error
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @brief Construct an Error object with no context or description.
|
|
||||||
*/
|
|
||||||
Error() { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Construct an Error object using a context and description.
|
|
||||||
*
|
|
||||||
* @param context Context string to use
|
|
||||||
* @param description Description string to use
|
|
||||||
*/
|
|
||||||
Error(const std::vector<std::string> &context, const std::string &description)
|
|
||||||
: context(context),
|
|
||||||
description(description) { }
|
|
||||||
|
|
||||||
/// Path to the node that failed validation.
|
/// Path to the node that failed validation.
|
||||||
std::vector<std::string> context;
|
std::vector<std::string> context;
|
||||||
|
|
||||||
@ -52,7 +38,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
std::deque<Error>::const_iterator begin() const
|
std::deque<Error>::const_iterator begin() const
|
||||||
{
|
{
|
||||||
return errors.begin();
|
return m_errors.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,7 +46,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
std::deque<Error>::const_iterator end() const
|
std::deque<Error>::const_iterator end() const
|
||||||
{
|
{
|
||||||
return errors.end();
|
return m_errors.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,7 +54,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
size_t numErrors() const
|
size_t numErrors() const
|
||||||
{
|
{
|
||||||
return errors.size();
|
return m_errors.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,7 +64,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
void pushError(const Error &error)
|
void pushError(const Error &error)
|
||||||
{
|
{
|
||||||
errors.push_back(error);
|
m_errors.push_back(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,7 +76,7 @@ public:
|
|||||||
void
|
void
|
||||||
pushError(const std::vector<std::string> &context, const std::string &description)
|
pushError(const std::vector<std::string> &context, const std::string &description)
|
||||||
{
|
{
|
||||||
errors.push_back(Error(context, description));
|
m_errors.push_back({context, description});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,20 +89,19 @@ public:
|
|||||||
bool
|
bool
|
||||||
popError(Error &error)
|
popError(Error &error)
|
||||||
{
|
{
|
||||||
if (errors.empty()) {
|
if (m_errors.empty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = errors.front();
|
error = m_errors.front();
|
||||||
errors.pop_front();
|
m_errors.pop_front();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/// FIFO queue of validation errors that have been reported
|
/// FIFO queue of validation errors that have been reported
|
||||||
std::deque<Error> errors;
|
std::deque<Error> m_errors;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace valijson
|
} // namespace valijson
|
||||||
|
Loading…
x
Reference in New Issue
Block a user