json/schema: add pattern_properties and associated tests
This commit is contained in:
parent
85b5853f7f
commit
8c222300ca
@ -29,13 +29,15 @@ using util::json::schema::constraint::base;
|
||||
std::unique_ptr<base>
|
||||
base::instantiate (std::string const &name, ::json::tree::node const &def)
|
||||
{
|
||||
if (name == "type") return std::make_unique<type> (def);
|
||||
if (name == "minLength") return std::make_unique<min_length> (def);
|
||||
if (name == "maxLength") return std::make_unique<max_length> (def);
|
||||
if (name == "minimum") return std::make_unique<minimum> (def);
|
||||
if (name == "maximum") return std::make_unique<maximum> (def);
|
||||
if (name == "properties") return std::make_unique<properties> (def);
|
||||
if (name == "enum") return std::make_unique<enumeration> (def);
|
||||
if (name == "type") return std::make_unique<type> (def);
|
||||
if (name == "minLength") return std::make_unique<min_length> (def);
|
||||
if (name == "maxLength") return std::make_unique<max_length> (def);
|
||||
if (name == "minimum") return std::make_unique<minimum> (def);
|
||||
if (name == "maximum") return std::make_unique<maximum> (def);
|
||||
if (name == "properties") return std::make_unique<properties> (def);
|
||||
if (name == "patternProperties") return std::make_unique<pattern_properties> (def);
|
||||
if (name == "additionalProperties") return std::make_unique<additional_properties> (def);
|
||||
if (name == "enum") return std::make_unique<enumeration> (def);
|
||||
|
||||
throw unknown_constraint (name);
|
||||
}
|
||||
|
@ -27,4 +27,6 @@ namespace util::json::schema::constraint {
|
||||
template <typename> class length;
|
||||
template <template <typename> class> class inequality;
|
||||
class properties;
|
||||
class pattern_properties;
|
||||
class additional_properties;
|
||||
}
|
@ -29,7 +29,7 @@ namespace util {
|
||||
};
|
||||
|
||||
|
||||
MAP0(SCHEMA_INTROSPECTION, type, combine, properties)
|
||||
MAP0(SCHEMA_INTROSPECTION, type, combine, properties, pattern_properties, additional_properties)
|
||||
|
||||
#undef SCHEMA_INTROSPECTION
|
||||
}
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include "../tree.hpp"
|
||||
|
||||
using util::json::schema::constraint::properties;
|
||||
using util::json::schema::constraint::pattern_properties;
|
||||
using util::json::schema::constraint::additional_properties;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -33,7 +35,7 @@ properties::properties (::json::tree::node const &def)
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
properties::output_iterator
|
||||
properties::validate (output_iterator res, ::json::tree::node &target) const noexcept
|
||||
{
|
||||
@ -65,7 +67,7 @@ properties::validate (output_iterator res, ::json::tree::node &target) const noe
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
std::ostream&
|
||||
properties::describe (std::ostream &os) const
|
||||
{
|
||||
@ -79,3 +81,81 @@ properties::describe (std::ostream &os) const
|
||||
|
||||
return os << " ] }";
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
pattern_properties::pattern_properties (::json::tree::node const &def)
|
||||
{
|
||||
if (!def.is_object ())
|
||||
throw constraint_error<pattern_properties> (def);
|
||||
|
||||
for (auto const &[key,val]: def.as_object ()) {
|
||||
m_patterns.push_back (pattern_t {
|
||||
.pattern = key,
|
||||
.matcher = std::regex (key),
|
||||
.validator = *val
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
pattern_properties::output_iterator
|
||||
pattern_properties::validate (output_iterator res,
|
||||
::json::tree::node &target) const noexcept
|
||||
{
|
||||
if (!target.is_object ())
|
||||
return *res++ = { .rule = *this, .target = target };
|
||||
|
||||
// validate each key and value in turn
|
||||
for (auto &[key, val]: target.as_object ()) {
|
||||
// find any regex matches and forward the validation onwards
|
||||
for (auto const &[pattern,regex,validator]: m_patterns) {
|
||||
if (std::regex_search (key, regex)) {
|
||||
res = validator.validate (res, *val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
std::ostream&
|
||||
pattern_properties::describe (std::ostream &os) const
|
||||
{
|
||||
os << "{ pattern_properties: { ";
|
||||
for (auto const &[pattern,regex,validator]: m_patterns)
|
||||
os << '"' << pattern << '"' << ": " << validator << ", ";
|
||||
return os << " } }";
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
additional_properties::additional_properties (::json::tree::node const &def):
|
||||
m_fallback (def)
|
||||
{ }
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
additional_properties::output_iterator
|
||||
additional_properties::validate (output_iterator res,
|
||||
::json::tree::node &target) const noexcept
|
||||
{
|
||||
if (!target.is_object ())
|
||||
return *res++ = { .rule = *this, .target = target };
|
||||
|
||||
for (auto &[key,val]: target.as_object ())
|
||||
res = m_fallback.validate (res, *val);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
std::ostream&
|
||||
additional_properties::describe (std::ostream &os) const
|
||||
{
|
||||
return os << "{ additional_properties: " << m_fallback << " }";
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -37,4 +38,39 @@ namespace util::json::schema::constraint {
|
||||
private:
|
||||
std::map<std::string,combine> m_properties;
|
||||
};
|
||||
|
||||
|
||||
class pattern_properties : public base {
|
||||
public:
|
||||
pattern_properties (::json::tree::node const&);
|
||||
|
||||
output_iterator validate (output_iterator, ::json::tree::node &target) const noexcept override;
|
||||
std::ostream& describe (std::ostream&) const override;
|
||||
|
||||
private:
|
||||
struct pattern_t {
|
||||
std::string pattern;
|
||||
std::regex matcher;
|
||||
combine validator;
|
||||
};
|
||||
|
||||
std::vector<pattern_t> m_patterns;
|
||||
};
|
||||
|
||||
|
||||
class additional_properties : public base {
|
||||
public:
|
||||
additional_properties (::json::tree::node const&, properties*, pattern_properties*);
|
||||
additional_properties (::json::tree::node const&);
|
||||
|
||||
virtual ~additional_properties () = default;
|
||||
|
||||
output_iterator validate (output_iterator, ::json::tree::node &target) const noexcept override;
|
||||
std::ostream& describe (std::ostream&) const override;
|
||||
|
||||
private:
|
||||
combine m_fallback;
|
||||
properties *m_properties = nullptr;
|
||||
pattern_properties *m_patterns = nullptr;
|
||||
};
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
{ "sinteger": 0 }
|
@ -0,0 +1 @@
|
||||
{ "istring": "value" }
|
@ -0,0 +1 @@
|
||||
{ "Foo": 1 }
|
@ -0,0 +1 @@
|
||||
{ "iFoo": 0 }
|
@ -0,0 +1 @@
|
||||
{ "sFoo": "var" }
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"sFoo": null
|
||||
}
|
@ -0,0 +1 @@
|
||||
{ "s": "value" }
|
@ -0,0 +1 @@
|
||||
{ "skey": "value" }
|
@ -0,0 +1 @@
|
||||
{ "i": 0 }
|
@ -0,0 +1 @@
|
||||
{ "ikey": 0 }
|
@ -0,0 +1 @@
|
||||
{ "thisEndsWithFoo": "foo" }
|
@ -0,0 +1 @@
|
||||
{ "thisEndsWithFoo": null }
|
@ -0,0 +1 @@
|
||||
{ "sFoo": "foo" }
|
@ -0,0 +1,7 @@
|
||||
{
|
||||
"patternProperties": {
|
||||
"^s": { "type": "string" },
|
||||
"^i": { "type": "integer" },
|
||||
"Foo$": { "enum": [ "foo", null ] }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user