Skip to content

Commit 7c39c01

Browse files
committed
Verify no extra keys in object
1 parent 9d9f493 commit 7c39c01

File tree

1 file changed

+31
-23
lines changed

1 file changed

+31
-23
lines changed

src/jse/jse.cpp

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ namespace jse
6969

7070
} // namespace
7171

72-
7372
// enriches a given json spec with included json specs
7473
json JSE::inject_include(const json &rules)
7574
{
@@ -83,20 +82,20 @@ namespace jse
8382
{
8483
// check if the rules have any include
8584
bool include_present = false;
86-
for (const auto& rule : current)
85+
for (const auto &rule : current)
8786
if (rule.at("type") == "include")
8887
include_present = true;
89-
88+
9089
// if there are no includes, return the current ones
9190
if (!include_present)
9291
return current;
9392

9493
json enriched;
9594
// otherwise, do a round of replacement
96-
for (const auto& rule : current)
95+
for (const auto &rule : current)
9796
{
9897
// copy all rules that are not include
99-
if (rule.at("type") != "include")
98+
if (rule.at("type") != "include")
10099
{
101100
enriched.push_back(rule);
102101
}
@@ -105,7 +104,7 @@ namespace jse
105104
{
106105
bool replaced = false;
107106
// the include file could be in any of the include directories
108-
for (const auto& dir : dirs)
107+
for (const auto &dir : dirs)
109108
{
110109
string spec_file = rule.at("spec_file");
111110
string f = dir + "/" + spec_file;
@@ -116,23 +115,22 @@ namespace jse
116115
json include_rules = json::parse(ifs);
117116

118117
// loop over all rules to add the prefix
119-
for (auto& i_rule : include_rules)
118+
for (auto &i_rule : include_rules)
120119
{
121120
string prefix = rule.at("pointer");
122121
string pointer = i_rule.at("pointer");
123-
string new_pointer = prepend_pointer(pointer,prefix);
122+
string new_pointer = prepend_pointer(pointer, prefix);
124123
i_rule.at("pointer") = new_pointer;
125124
}
126125

127126
// save modified rules
128-
for (const auto& i_rule : include_rules)
127+
for (const auto &i_rule : include_rules)
129128
enriched.push_back(i_rule);
130129

131130
// one substitution is enough, give up the search over include dirs
132131
replaced = true;
133132
break;
134133
}
135-
136134
}
137135

138136
if (!replaced)
@@ -149,10 +147,8 @@ namespace jse
149147
}
150148

151149
throw std::runtime_error("Reached maximal 10 levels of include recursion.");
152-
153150
}
154151

155-
156152
bool JSE::verify_json(const string &pointer, json &input, const json &rules)
157153
{
158154
// Find all rules that apply for the input node
@@ -167,19 +163,16 @@ namespace jse
167163
}
168164

169165
// Test all rules, one and only one must pass, otherwise throw exception
170-
int count = 0;
171-
json single_matched_rule;
172-
173-
for (auto i : matching_rules)
166+
std::vector<json> verified_matching_rules;
167+
for (auto r : matching_rules)
174168
{
175-
if (verify_rule(input, i))
169+
if (verify_rule(input, r))
176170
{
177-
count++;
178-
single_matched_rule = i;
171+
verified_matching_rules.push_back(r);
179172
}
180173
}
181174

182-
if (count == 0)
175+
if (verified_matching_rules.size() == 0)
183176
{
184177
// Before giving up, try boxing a primitive type
185178
if (boxing_primitive && !input.is_array())
@@ -199,16 +192,17 @@ namespace jse
199192
log.push_back(log_item("error", s.str()));
200193
return false;
201194
}
202-
else if (count > 1)
195+
else if (verified_matching_rules.size() > 1)
203196
{
204197
std::stringstream s;
205198
s << "Multiple rules matched for \"" << pointer << "\": " << input.dump(/*indent=*/4) << std::endl;
206199
s << "Multiple valid rules in this list, only one should be valid:";
207-
for (int i = 0; i < matching_rules.size(); i++)
208-
s << i << ": " << matching_rules[i].dump(/*indent=*/4) << "\n";
200+
for (int i = 0; i < verified_matching_rules.size(); i++)
201+
s << i << ": " << verified_matching_rules[i].dump(/*indent=*/4) << "\n";
209202
log.push_back(log_item("error", s.str()));
210203
return false;
211204
}
205+
const json &single_matched_rule = verified_matching_rules.front();
212206

213207
// If it passes and if it is a dictionary, then test all childrens
214208
if (input.is_object())
@@ -427,11 +421,25 @@ namespace jse
427421
if (!input.is_object() && !input.is_null())
428422
return false;
429423

424+
// Check that all required fields are present
430425
if (rule.contains("required"))
431426
for (auto e : rule["required"])
432427
if (!input.contains(string(e)))
433428
return false;
434429

430+
std::vector<std::string> keys;
431+
keys.reserve((rule.contains("required") ? rule["required"].size() : 0)
432+
+ (rule.contains("optional") ? rule["optional"].size() : 0));
433+
if (rule.contains("required"))
434+
keys.insert(keys.end(), rule["required"].begin(), rule["required"].end());
435+
if (rule.contains("optional"))
436+
keys.insert(keys.end(), rule["optional"].begin(), rule["optional"].end());
437+
438+
// Check that no extra fields are present
439+
for (const auto &[key, value] : input.items())
440+
if (std::find(keys.begin(), keys.end(), key) == keys.end())
441+
return false;
442+
435443
if (rule.contains("type_name")
436444
&& (!input.contains("type") || input["type"] != rule["type_name"]))
437445
return false;

0 commit comments

Comments
 (0)