Skip to content

Commit b901236

Browse files
Merge branch 'inject_default' into main
2 parents 8f0ae2f + 0c360c2 commit b901236

File tree

4 files changed

+350
-31
lines changed

4 files changed

+350
-31
lines changed

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"type": "cppdbg",
77
"request": "launch",
88
"program": "/home/daniele/git/simple-json-validator/build/tests/unit_tests",
9-
"args": ["type_string"],
9+
"args": [""],
1010
"stopAtEntry": false,
1111
"cwd": "/home/daniele/git/simple-json-validator/build/",
1212
"environment": [],

src/sjv/sjv.cpp

Lines changed: 166 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,80 @@
33
#include <iostream>
44
#include <filesystem> // C++17
55
#include <sstream>
6+
#include <algorithm>
67
////////////////////////////////////////////////////////////////////////////////
78

89
namespace sjv
910
{
11+
12+
//////////// PUBLIC
13+
1014
bool SJV::verify_json(const json &input, const json &rules)
1115
{
1216
log.clear();
1317
return verify_json("/", input, rules);
14-
}
18+
};
19+
20+
json SJV::inject_defaults(const json &input, const json &rules)
21+
{
22+
// The code below assumes that the input satisfies the rules
23+
assert(verify_json(input, rules));
24+
25+
// Find all the default rules
26+
json default_rules = collect_default_rules(rules);
27+
28+
// Flatten the input
29+
json flat = input.flatten();
30+
json out_flat = flat;
31+
32+
// For each rule, go over all entries of a flattened input
33+
for (auto rule : default_rules)
34+
{
35+
for (auto e : flat.items())
36+
{
37+
// If the pointer matches a strict subset of it, add it to the flattened
38+
std::tuple<bool, string> subset = is_subset_pointer(e.key(), string(rule["pointer"]));
39+
if (std::get<0>(subset))
40+
if (!out_flat.contains(std::get<1>(subset)))
41+
out_flat[std::get<1>(subset)] = rule["default"];
42+
43+
// Try it also without the last entry to capture object types which are empty
44+
string key_parent = e.key().substr(0,e.key().find_last_of("/\\"));
45+
46+
subset = is_subset_pointer(key_parent, string(rule["pointer"]));
47+
if (std::get<0>(subset))
48+
if (!out_flat.contains(std::get<1>(subset)))
49+
out_flat[std::get<1>(subset)] = rule["default"];
50+
51+
}
52+
// Special case as "/" is not inserted in the flat representation
53+
std::tuple<bool, string> subset = is_subset_pointer("/", string(rule["pointer"]));
54+
if (std::get<0>(subset))
55+
if (!out_flat.contains(std::get<1>(subset)))
56+
out_flat[std::get<1>(subset)] = rule["default"];
57+
}
58+
59+
// Unflatten the input
60+
json output = out_flat.unflatten();
61+
62+
// Certify the validity of the final file
63+
assert(verify_json(input, rules));
64+
65+
return output;
66+
};
67+
68+
std::string SJV::log2str()
69+
{
70+
std::stringstream s;
71+
72+
for (log_item i : log)
73+
{
74+
s << i.first << ": " << i.second << std::endl;
75+
}
76+
return s.str();
77+
};
78+
79+
//////////// PRIVATE
1580

1681
bool SJV::verify_json(const string &pointer, const json &input, const json &rules)
1782
{
@@ -26,6 +91,8 @@ namespace sjv
2691

2792
if (strict)
2893
return false;
94+
else
95+
return true;
2996
}
3097

3198
// Test all rules, only one must pass, otherwise throw exception
@@ -35,7 +102,7 @@ namespace sjv
35102
if (verify_rule(input, i))
36103
count++;
37104

38-
if (count == 0 && !matching_rules.empty())
105+
if (count == 0 && !matching_rules.empty())
39106
{
40107
// Before giving up, try boxing a primitive type
41108
if (boxing_primitive && !input.is_array())
@@ -44,7 +111,7 @@ namespace sjv
44111
// Make sure there are some rules for the boxed version before recursively checking
45112
if (collect_pointer(new_pointer, rules).size() > 0)
46113
if (verify_json(new_pointer, input, rules))
47-
return true;
114+
return true;
48115
}
49116

50117
std::stringstream s;
@@ -274,17 +341,21 @@ namespace sjv
274341
return false;
275342

276343
return true;
277-
}
344+
};
278345

279-
std::string SJV::log2str()
346+
json SJV::collect_default_rules(const json &rules)
280347
{
281-
std::stringstream s;
348+
// Find all rules that apply for the input node
349+
// TODO: accelerate this
282350

283-
for (log_item i : log)
351+
std::vector<json> matching_rules;
352+
for (auto i : rules)
284353
{
285-
s << i.first << ": " << i.second << std::endl;
354+
if (i.contains("default"))
355+
matching_rules.push_back(i);
286356
}
287-
return s.str();
357+
358+
return matching_rules;
288359
};
289360

290361
json SJV::collect_default_rules(const string &pointer, const json &rules)
@@ -307,7 +378,7 @@ namespace sjv
307378
return std::find(list.begin(), list.end(), item) != list.end();
308379
};
309380

310-
std::vector<json> SJV::collect_pointer(const string& pointer, const json& rules)
381+
std::vector<json> SJV::collect_pointer(const string &pointer, const json &rules)
311382
{
312383
std::vector<json> matching_rules;
313384
for (auto i : rules)
@@ -317,4 +388,89 @@ namespace sjv
317388
}
318389
return matching_rules;
319390
};
391+
392+
json SJV::find_valid_rule(const string &pointer, const json &input, const json &rules)
393+
{
394+
for (auto i : collect_pointer(pointer, rules))
395+
if (verify_rule(input, i))
396+
return i;
397+
398+
return json();
399+
};
400+
401+
std::tuple<bool, string> SJV::is_subset_pointer(const string &json, const string &pointer)
402+
{
403+
// Splits a string into tokens using the deliminator delim
404+
auto tokenize = [](std::string const &str, const char delim) {
405+
size_t start;
406+
size_t end = 0;
407+
std::vector<string> out;
408+
409+
while ((start = str.find_first_not_of(delim, end)) != std::string::npos)
410+
{
411+
end = str.find(delim, start);
412+
out.push_back(str.substr(start, end - start));
413+
}
414+
415+
return out;
416+
};
417+
418+
// Replaces occurrences of a substring
419+
auto replace_all = [](std::string str, const std::string &from, const std::string &to) {
420+
size_t start_pos = 0;
421+
while ((start_pos = str.find(from, start_pos)) != std::string::npos)
422+
{
423+
str.replace(start_pos, from.length(), to);
424+
start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
425+
}
426+
return str;
427+
};
428+
429+
// Check if a string is an integer
430+
auto is_number = [](const string &str) {
431+
for (char const &c : str)
432+
if (std::isdigit(c) == 0)
433+
return false;
434+
return true;
435+
};
436+
437+
// Tokenize json_pointer
438+
std::vector<string> json_t = tokenize(json, '/');
439+
std::vector<string> pointer_t = tokenize(pointer, '/');
440+
441+
// if the json is not shorter or if there are no tokens, give up
442+
if ((json_t.size() >= pointer_t.size())
443+
|| (pointer_t.size() == 0))
444+
return {false, ""};
445+
446+
std::string buf = "";
447+
// if it is shorter, match every entry
448+
for (unsigned i = 0; i < pointer_t.size(); ++i)
449+
{
450+
// if there is no corresponding entry on json, copy and move on
451+
if (json_t.size() <= i)
452+
{
453+
buf.append("/" + replace_all(pointer_t[i], "*", "0"));
454+
}
455+
// if there is an entry on json and it is the same, move on
456+
else if (json_t[i] == pointer_t[i])
457+
{
458+
buf.append("/" + pointer_t[i]);
459+
}
460+
// if the pointer contains a star, then accept any integer on json
461+
else if (pointer_t[i] == "*")
462+
{
463+
if (is_number(json_t[i]))
464+
buf.append("/" + json_t[i]);
465+
}
466+
// if no rule matches it is not a match
467+
else
468+
{
469+
return {false, ""};
470+
}
471+
}
472+
473+
return {true, buf};
474+
};
475+
320476
} // namespace sjv

src/sjv/sjv.h

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,11 @@ namespace sjv
1515
// verify the input json against the set of rules in specs
1616
bool verify_json(const json &input, const json &rules);
1717

18-
// Verify a node pointed by
19-
bool verify_json(const string &pointer, const json &input, const json &rules);
20-
21-
// Dispatcher for rule verification
22-
bool verify_rule(const json &input, const json &rule);
18+
// enriches a given json with default values
19+
json inject_defaults(const json &input, const json &rules);
2320

24-
// Type-specific rule handlers
25-
bool verify_rule_file(const json &input, const json &rule);
26-
bool verify_rule_folder(const json &input, const json &rule);
27-
bool verify_rule_float(const json &input, const json &rule);
28-
bool verify_rule_int(const json &input, const json &rule);
29-
bool verify_rule_string(const json &input, const json &rule);
30-
bool verify_rule_object(const json &input, const json &rule);
31-
bool verify_rule_bool(const json &input, const json &rule);
32-
bool verify_rule_list(const json &input, const json &rule);
33-
34-
// TODO
35-
json generate_default_json(const json &rules);
21+
// log to string
22+
std::string log2str();
3623

3724
// Working directory
3825
string cwd = ".";
@@ -48,17 +35,44 @@ namespace sjv
4835
typedef std::pair<std::string, std::string> log_item;
4936
std::vector<log_item> log;
5037

51-
// log to string
52-
std::string log2str();
38+
private:
39+
// enriches a given json with default values
40+
void inject_defaults(const string &pointer, const json &input, const json &rules, json &output);
41+
42+
// Verify a node pointed by
43+
bool verify_json(const string &pointer, const json &input, const json &rules);
44+
45+
// Dispatcher for rule verification
46+
bool verify_rule(const json &input, const json &rule);
47+
48+
// Type-specific rule handlers
49+
bool verify_rule_file(const json &input, const json &rule);
50+
bool verify_rule_folder(const json &input, const json &rule);
51+
bool verify_rule_float(const json &input, const json &rule);
52+
bool verify_rule_int(const json &input, const json &rule);
53+
bool verify_rule_string(const json &input, const json &rule);
54+
bool verify_rule_object(const json &input, const json &rule);
55+
bool verify_rule_bool(const json &input, const json &rule);
56+
bool verify_rule_list(const json &input, const json &rule);
57+
58+
// Collect all rules having a default
59+
json collect_default_rules(const json &rules);
5360

5461
// Collect all rules having a default for a given pointer
5562
json collect_default_rules(const string &pointer, const json &rules);
5663

5764
// Collect all rules having a given pointer
5865
std::vector<json> collect_pointer(const string &pointer, const json &rules);
5966

67+
// Find the first rule matching a pointer
68+
json find_valid_rule(const string &pointer, const json &input, const json &rules);
69+
6070
// Utils
6171
bool contained_in_list(string item, const json &list);
72+
73+
// Checks if a given json pointer is a subset of a pointer string (containing wildcards).
74+
// If it is, the second return parameter is an instantiated pointer
75+
std::tuple<bool, string> is_subset_pointer(const string &json_pointer, const string &pointer);
6276
};
6377

6478
} // namespace sjv

0 commit comments

Comments
 (0)