mirror of
https://github.com/tristanpenman/valijson.git
synced 2025-03-04 19:13:30 +01:00
Merge pull request #160 from jrave/time-format-fields
This commit is contained in:
commit
25dcdb1c3a
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,3 +7,4 @@ doc/html
|
|||||||
.idea
|
.idea
|
||||||
cmake-build-*
|
cmake-build-*
|
||||||
CMakeFiles/
|
CMakeFiles/
|
||||||
|
.vs
|
||||||
|
@ -180,6 +180,10 @@ if(valijson_BUILD_TESTS)
|
|||||||
set_target_properties(test_suite PROPERTIES COMPILE_FLAGS " -pedantic -Werror -Wshadow -Wunused")
|
set_target_properties(test_suite PROPERTIES COMPILE_FLAGS " -pedantic -Werror -Wshadow -Wunused")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
target_compile_options(test_suite PRIVATE "/bigobj")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Definition for using picojson
|
# Definition for using picojson
|
||||||
set_target_properties(test_suite PROPERTIES COMPILE_DEFINITIONS "PICOJSON_USE_INT64")
|
set_target_properties(test_suite PROPERTIES COMPILE_DEFINITIONS "PICOJSON_USE_INT64")
|
||||||
|
|
||||||
|
@ -427,6 +427,33 @@ private:
|
|||||||
EnumValues m_enumValues;
|
EnumValues m_enumValues;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Represent a 'format' constraint
|
||||||
|
*
|
||||||
|
* A format constraint restricts the content of string values, as defined by a set of commonly used formats.
|
||||||
|
*
|
||||||
|
* As this is an optional feature in JSON Schema, unrecognised formats will be treated as valid for any string value.
|
||||||
|
*/
|
||||||
|
class FormatConstraint: public BasicConstraint<FormatConstraint>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FormatConstraint()
|
||||||
|
: m_format() { }
|
||||||
|
|
||||||
|
const std::string & getFormat() const
|
||||||
|
{
|
||||||
|
return m_format;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFormat(const std::string & format)
|
||||||
|
{
|
||||||
|
m_format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_format;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Represents non-singular 'items' and 'additionalItems' constraints
|
* @brief Represents non-singular 'items' and 'additionalItems' constraints
|
||||||
*
|
*
|
||||||
|
@ -10,6 +10,7 @@ class ConstConstraint;
|
|||||||
class ContainsConstraint;
|
class ContainsConstraint;
|
||||||
class DependenciesConstraint;
|
class DependenciesConstraint;
|
||||||
class EnumConstraint;
|
class EnumConstraint;
|
||||||
|
class FormatConstraint;
|
||||||
class LinearItemsConstraint;
|
class LinearItemsConstraint;
|
||||||
class MaxItemsConstraint;
|
class MaxItemsConstraint;
|
||||||
class MaximumConstraint;
|
class MaximumConstraint;
|
||||||
@ -46,6 +47,7 @@ protected:
|
|||||||
typedef constraints::ContainsConstraint ContainsConstraint;
|
typedef constraints::ContainsConstraint ContainsConstraint;
|
||||||
typedef constraints::DependenciesConstraint DependenciesConstraint;
|
typedef constraints::DependenciesConstraint DependenciesConstraint;
|
||||||
typedef constraints::EnumConstraint EnumConstraint;
|
typedef constraints::EnumConstraint EnumConstraint;
|
||||||
|
typedef constraints::FormatConstraint FormatConstraint;
|
||||||
typedef constraints::LinearItemsConstraint LinearItemsConstraint;
|
typedef constraints::LinearItemsConstraint LinearItemsConstraint;
|
||||||
typedef constraints::MaximumConstraint MaximumConstraint;
|
typedef constraints::MaximumConstraint MaximumConstraint;
|
||||||
typedef constraints::MaxItemsConstraint MaxItemsConstraint;
|
typedef constraints::MaxItemsConstraint MaxItemsConstraint;
|
||||||
@ -77,6 +79,7 @@ public:
|
|||||||
virtual bool visit(const ContainsConstraint &) = 0;
|
virtual bool visit(const ContainsConstraint &) = 0;
|
||||||
virtual bool visit(const DependenciesConstraint &) = 0;
|
virtual bool visit(const DependenciesConstraint &) = 0;
|
||||||
virtual bool visit(const EnumConstraint &) = 0;
|
virtual bool visit(const EnumConstraint &) = 0;
|
||||||
|
virtual bool visit(const FormatConstraint &) = 0;
|
||||||
virtual bool visit(const LinearItemsConstraint &) = 0;
|
virtual bool visit(const LinearItemsConstraint &) = 0;
|
||||||
virtual bool visit(const MaximumConstraint &) = 0;
|
virtual bool visit(const MaximumConstraint &) = 0;
|
||||||
virtual bool visit(const MaxItemsConstraint &) = 0;
|
virtual bool visit(const MaxItemsConstraint &) = 0;
|
||||||
|
@ -694,6 +694,10 @@ private:
|
|||||||
rootSchema.addConstraintToSubschema(makeEnumConstraint(itr->second), &subschema);
|
rootSchema.addConstraintToSubschema(makeEnumConstraint(itr->second), &subschema);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((itr = object.find("format")) != object.end()) {
|
||||||
|
rootSchema.addConstraintToSubschema(makeFormatConstraint(itr->second), &subschema);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const typename AdapterType::Object::const_iterator itemsItr =
|
const typename AdapterType::Object::const_iterator itemsItr =
|
||||||
object.find("items");
|
object.find("items");
|
||||||
@ -1421,6 +1425,29 @@ private:
|
|||||||
return constraint;
|
return constraint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Make a new FormatConstraint object
|
||||||
|
*
|
||||||
|
* @param node JSON node containing the configuration for this constraint
|
||||||
|
*
|
||||||
|
* @return pointer to a new FormatConstraint that belongs to the caller
|
||||||
|
*/
|
||||||
|
template<typename AdapterType>
|
||||||
|
constraints::FormatConstraint makeFormatConstraint(
|
||||||
|
const AdapterType &node)
|
||||||
|
{
|
||||||
|
if (node.isString()) {
|
||||||
|
const std::string value = node.asString();
|
||||||
|
if (!value.empty()) {
|
||||||
|
constraints::FormatConstraint constraint;
|
||||||
|
constraint.setFormat(value);
|
||||||
|
return constraint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throwRuntimeError("Expected a string value for 'format' constraint.");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Make a new ItemsConstraint object.
|
* @brief Make a new ItemsConstraint object.
|
||||||
*
|
*
|
||||||
|
@ -347,6 +347,64 @@ public:
|
|||||||
return numValidated > 0;
|
return numValidated > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Validate current node against a FormatConstraint
|
||||||
|
*
|
||||||
|
* @param constraint Constraint that the target must validate against
|
||||||
|
*
|
||||||
|
* @return \c true if validation succeeds; \c false otherwise
|
||||||
|
*/
|
||||||
|
bool visit(const FormatConstraint &constraint) override
|
||||||
|
{
|
||||||
|
const std::string s = m_target.asString();
|
||||||
|
const std::string format = constraint.getFormat();
|
||||||
|
if (format == "date") {
|
||||||
|
// Matches dates like: 2022-07-18
|
||||||
|
std::regex date_regex("^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$");
|
||||||
|
std::smatch matches;
|
||||||
|
if (std::regex_match(s, matches, date_regex)) {
|
||||||
|
const auto month = std::stoi(matches[2].str());
|
||||||
|
const auto day = std::stoi(matches[3].str());
|
||||||
|
return validate_date_range(month, day);
|
||||||
|
} else {
|
||||||
|
if (m_results) {
|
||||||
|
m_results->pushError(m_context,
|
||||||
|
"String should be a valid date");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (format == "time") {
|
||||||
|
// Matches times like: 16:52:45Z, 16:52:45+02:00
|
||||||
|
std::regex time_regex("^([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([Zz])|([\+|\-]([01][0-9]|2[0-3]):[0-5][0-9]))$");
|
||||||
|
if (std::regex_match(s, time_regex)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (m_results) {
|
||||||
|
m_results->pushError(m_context,
|
||||||
|
"String should be a valid time");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (format == "date-time") {
|
||||||
|
// Matches data times like: 2022-07-18T16:52:45Z, 2022-07-18T16:52:45+02:00
|
||||||
|
std::regex datetime_regex("^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([Zz])|([\+|\-]([01][0-9]|2[0-3]):[0-5][0-9]))$");
|
||||||
|
std::smatch matches;
|
||||||
|
if (std::regex_match(s, matches, datetime_regex)) {
|
||||||
|
const auto month = std::stoi(matches[2].str());
|
||||||
|
const auto day = std::stoi(matches[3].str());
|
||||||
|
return validate_date_range(month, day);
|
||||||
|
} else {
|
||||||
|
if (m_results) {
|
||||||
|
m_results->pushError(m_context,
|
||||||
|
"String should be a valid date-time");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Validate a value against a LinearItemsConstraint
|
* @brief Validate a value against a LinearItemsConstraint
|
||||||
*
|
*
|
||||||
@ -1769,6 +1827,47 @@ private:
|
|||||||
return constraint.accept(visitor);
|
return constraint.accept(visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Helper function to validate if day is valid for given month
|
||||||
|
*
|
||||||
|
* @param month Month, 1-12
|
||||||
|
* @param day Day, 1-31
|
||||||
|
*
|
||||||
|
* @return true if day is valid for given month, false otherwise.
|
||||||
|
*/
|
||||||
|
bool validate_date_range(int month, int day)
|
||||||
|
{
|
||||||
|
if (month == 2) {
|
||||||
|
if (day < 0 || day > 29) {
|
||||||
|
if (m_results) {
|
||||||
|
m_results->pushError(m_context,
|
||||||
|
"String should be a valid date-time");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int limit = 31;
|
||||||
|
if (month <= 7) {
|
||||||
|
if (month % 2 == 0) {
|
||||||
|
limit = 30;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (month % 2 != 0) {
|
||||||
|
limit = 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (day < 0 || day > limit) {
|
||||||
|
if (m_results) {
|
||||||
|
m_results->pushError(m_context,
|
||||||
|
"String should be a valid date-time");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// The JSON value being validated
|
/// The JSON value being validated
|
||||||
AdapterType m_target;
|
AdapterType m_target;
|
||||||
|
|
||||||
|
@ -15,10 +15,12 @@
|
|||||||
#include <valijson/adapters/jsoncpp_adapter.hpp>
|
#include <valijson/adapters/jsoncpp_adapter.hpp>
|
||||||
#include <valijson/adapters/rapidjson_adapter.hpp>
|
#include <valijson/adapters/rapidjson_adapter.hpp>
|
||||||
#include <valijson/adapters/picojson_adapter.hpp>
|
#include <valijson/adapters/picojson_adapter.hpp>
|
||||||
|
#include <valijson/adapters/nlohmann_json_adapter.hpp>
|
||||||
#include <valijson/utils/json11_utils.hpp>
|
#include <valijson/utils/json11_utils.hpp>
|
||||||
#include <valijson/utils/jsoncpp_utils.hpp>
|
#include <valijson/utils/jsoncpp_utils.hpp>
|
||||||
#include <valijson/utils/picojson_utils.hpp>
|
#include <valijson/utils/picojson_utils.hpp>
|
||||||
#include <valijson/utils/rapidjson_utils.hpp>
|
#include <valijson/utils/rapidjson_utils.hpp>
|
||||||
|
#include <valijson/utils/nlohmann_json_utils.hpp>
|
||||||
#include <valijson/schema.hpp>
|
#include <valijson/schema.hpp>
|
||||||
#include <valijson/schema_parser.hpp>
|
#include <valijson/schema_parser.hpp>
|
||||||
#include <valijson/validation_results.hpp>
|
#include <valijson/validation_results.hpp>
|
||||||
@ -176,6 +178,7 @@ protected:
|
|||||||
processTestFile<valijson::adapters::JsonCppAdapter>(testFile, version);
|
processTestFile<valijson::adapters::JsonCppAdapter>(testFile, version);
|
||||||
processTestFile<valijson::adapters::RapidJsonAdapter>(testFile, version);
|
processTestFile<valijson::adapters::RapidJsonAdapter>(testFile, version);
|
||||||
processTestFile<valijson::adapters::PicoJsonAdapter>(testFile, version);
|
processTestFile<valijson::adapters::PicoJsonAdapter>(testFile, version);
|
||||||
|
processTestFile<valijson::adapters::NlohmannJsonAdapter>(testFile, version);
|
||||||
|
|
||||||
#ifdef VALIJSON_BUILD_POCO_ADAPTER
|
#ifdef VALIJSON_BUILD_POCO_ADAPTER
|
||||||
processTestFile<valijson::adapters::PocoJsonAdapter>(testFile, version);
|
processTestFile<valijson::adapters::PocoJsonAdapter>(testFile, version);
|
||||||
@ -605,3 +608,18 @@ TEST_F(TestValidator, Draft7_UniqueItems)
|
|||||||
{
|
{
|
||||||
processDraft7TestFile(TEST_SUITE_DIR "draft7/uniqueItems.json");
|
processDraft7TestFile(TEST_SUITE_DIR "draft7/uniqueItems.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(TestValidator, Draft7_OptionalFormatDate)
|
||||||
|
{
|
||||||
|
processDraft7TestFile(TEST_SUITE_DIR "draft7/optional/format/date.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TestValidator, Draft7_OptionalFormatTime)
|
||||||
|
{
|
||||||
|
processDraft7TestFile(TEST_SUITE_DIR "draft7/optional/format/time.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TestValidator, Draft7_OptionalFormatDateTime)
|
||||||
|
{
|
||||||
|
processDraft7TestFile(TEST_SUITE_DIR "draft7/optional/format/date-time.json");
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user