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>
|
std::unique_ptr<base>
|
||||||
base::instantiate (std::string const &name, ::json::tree::node const &def)
|
base::instantiate (std::string const &name, ::json::tree::node const &def)
|
||||||
{
|
{
|
||||||
if (name == "type") return std::make_unique<type> (def);
|
if (name == "type") return std::make_unique<type> (def);
|
||||||
if (name == "minLength") return std::make_unique<min_length> (def);
|
if (name == "minLength") return std::make_unique<min_length> (def);
|
||||||
if (name == "maxLength") return std::make_unique<max_length> (def);
|
if (name == "maxLength") return std::make_unique<max_length> (def);
|
||||||
if (name == "minimum") return std::make_unique<minimum> (def);
|
if (name == "minimum") return std::make_unique<minimum> (def);
|
||||||
if (name == "maximum") return std::make_unique<maximum> (def);
|
if (name == "maximum") return std::make_unique<maximum> (def);
|
||||||
if (name == "properties") return std::make_unique<properties> (def);
|
if (name == "properties") return std::make_unique<properties> (def);
|
||||||
if (name == "enum") return std::make_unique<enumeration> (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);
|
throw unknown_constraint (name);
|
||||||
}
|
}
|
||||||
|
@ -27,4 +27,6 @@ namespace util::json::schema::constraint {
|
|||||||
template <typename> class length;
|
template <typename> class length;
|
||||||
template <template <typename> class> class inequality;
|
template <template <typename> class> class inequality;
|
||||||
class properties;
|
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
|
#undef SCHEMA_INTROSPECTION
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
#include "../tree.hpp"
|
#include "../tree.hpp"
|
||||||
|
|
||||||
using util::json::schema::constraint::properties;
|
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::output_iterator
|
||||||
properties::validate (output_iterator res, ::json::tree::node &target) const noexcept
|
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&
|
std::ostream&
|
||||||
properties::describe (std::ostream &os) const
|
properties::describe (std::ostream &os) const
|
||||||
{
|
{
|
||||||
@ -79,3 +81,81 @@ properties::describe (std::ostream &os) const
|
|||||||
|
|
||||||
return os << " ] }";
|
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 <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
@ -37,4 +38,39 @@ namespace util::json::schema::constraint {
|
|||||||
private:
|
private:
|
||||||
std::map<std::string,combine> m_properties;
|
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