Support for time related format fields

This commit is contained in:
Johannes Rave 2022-07-17 07:29:40 +03:00
parent 5f49d77b1e
commit dee2fa64ff
5 changed files with 124 additions and 11 deletions

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ doc/html
.idea .idea
cmake-build-* cmake-build-*
CMakeFiles/ CMakeFiles/
.vs

View File

@ -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")

View File

@ -1436,11 +1436,16 @@ private:
constraints::FormatConstraint makeFormatConstraint( constraints::FormatConstraint makeFormatConstraint(
const AdapterType &node) const AdapterType &node)
{ {
constraints::FormatConstraint constraint; if (node.isString()) {
const std::string value = node.asString();
if (!value.empty()) {
constraints::FormatConstraint constraint;
constraint.setFormat(value);
return constraint;
}
}
// TODO throwRuntimeError("Expected a string value for 'format' constraint.");
return constraint;
} }
/** /**

View File

@ -348,15 +348,59 @@ public:
} }
/** /**
* @brief Validate current node against a FormatConstraint * @brief Validate current node against a FormatConstraint
* *
* @param constraint Constraint that the target must validate against * @param constraint Constraint that the target must validate against
* *
* @return \c true if validation succeeds; \c false otherwise * @return \c true if validation succeeds; \c false otherwise
*/ */
bool visit(const FormatConstraint &constraint) override bool visit(const FormatConstraint &constraint) override
{ {
// TODO 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; return true;
} }
@ -1783,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;

View File

@ -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");
}