Skip to content

Commit ee36246

Browse files
Merge branch 'main' of github.com:geometryprocessing/simple-json-validator into main
2 parents e236ff5 + bf63ae3 commit ee36246

File tree

6 files changed

+128
-66
lines changed

6 files changed

+128
-66
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": ["file_type"],
9+
"args": ["type_string"],
1010
"stopAtEntry": false,
1111
"cwd": "/home/daniele/git/simple-json-validator/build/",
1212
"environment": [],

data/input_01.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
0,
88
0
99
],
10-
"rotation": -30,
10+
"rotation": [-30],
1111
"scale": [
1212
5,
1313
0.02
@@ -27,8 +27,8 @@
2727
0.26,
2828
0.4503
2929
],
30-
"rotation": -30,
31-
"scale": 1.0
30+
"rotation": [-30],
31+
"scale": [1.0]
3232
},
3333
"volume_selection": "../data/dummy.txt",
3434
"surface_selection": 2,

data/rules_01.json

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,74 +6,95 @@
66
"doc": "Root of the configuration file."
77
},
88
{
9-
"pointer": "/geometry/",
9+
"pointer": "/geometry",
10+
"type": "list",
11+
"min": 1,
12+
"doc": "List of /geometry objects."
13+
},
14+
{
15+
"pointer": "/geometry/*",
1016
"type": "object",
1117
"required": ["mesh"],
1218
"optional": ["transformation","volume_selection","surface_selection","n_refs","advanced"],
1319
"doc": "Each geometry object stores a mesh, a set of transformations applied to it after loading, and a set of selections, which can be used to specify boundary conditions and materials."
1420
},
1521
{
16-
"pointer": "/geometry/mesh/",
22+
"pointer": "/geometry/*/mesh",
1723
"type": "file",
1824
"extensions": [".obj",".msh"],
1925
"doc": "Path of the mesh file to load."
2026
},
2127
{
22-
"pointer": "/geometry/transformation/",
28+
"pointer": "/geometry/*/transformation",
2329
"type": "object",
2430
"default": null,
2531
"optional": ["translation","rotation","scale"],
2632
"doc": "Geometric transformations applied to the geometry after loading it."
2733
},
2834
{
29-
"pointer": "/geometry/transformation/translation/",
35+
"pointer": "/geometry/*/transformation/translation",
36+
"type": "list",
37+
"default": null
38+
},
39+
{
40+
"pointer": "/geometry/*/transformation/rotation",
41+
"type": "list",
42+
"default": null
43+
},
44+
{
45+
"pointer": "/geometry/*/transformation/scale",
46+
"type": "list",
47+
"default": null
48+
},
49+
{
50+
"pointer": "/geometry/*/transformation/translation/*",
3051
"type": "float",
3152
"default": [0,0,0],
3253
"doc": "Translation vector (2 entries for 2D problems, 3 entries for 3D problems)."
3354
},
3455
{
35-
"pointer": "/geometry/transformation/rotation/",
56+
"pointer": "/geometry/*/transformation/rotation/*",
3657
"default": 0,
3758
"type": "float"
3859
},
3960
{
40-
"pointer": "/geometry/transformation/scale/",
61+
"pointer": "/geometry/*/transformation/scale/*",
4162
"default": 0,
4263
"type": "float"
4364
},
4465
{
45-
"pointer": "/geometry/volume_selection/",
66+
"pointer": "/geometry/*/volume_selection",
4667
"type": "int",
4768
"default": 0
4869
},
4970
{
50-
"pointer": "/geometry/volume_selection/",
71+
"pointer": "/geometry/*/volume_selection",
5172
"type": "file",
5273
"extensions": [".txt"]
5374
},
5475
{
55-
"pointer": "/geometry/surface_selection/",
76+
"pointer": "/geometry/*/surface_selection",
5677
"type": "int",
5778
"default": null
5879
},
5980
{
60-
"pointer": "/geometry/surface_selection/",
81+
"pointer": "/geometry/*/surface_selection",
6182
"type": "file",
6283
"extensions": [".txt"]
6384
},
6485
{
65-
"pointer": "/geometry/n_refs/",
86+
"pointer": "/geometry/*/n_refs",
6687
"type": "int",
6788
"default": 0
6889
},
6990
{
70-
"pointer": "/geometry/advanced/",
91+
"pointer": "/geometry/*/advanced",
7192
"type": "object",
7293
"optional": ["normalize_mesh"],
7394
"default": null
7495
},
7596
{
76-
"pointer": "/geometry/advanced/normalize_mesh/",
97+
"pointer": "/geometry/*/advanced/normalize_mesh",
7798
"type": "bool",
7899
"default": false
79100
}

src/sjv/sjv.cpp

Lines changed: 63 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,9 @@ namespace sjv
1515

1616
bool SJV::verify_json(const string &pointer, const json &input, const json &rules)
1717
{
18-
// Polymorphism on list, a single item or a list are indistinguishable
19-
// All the elements in the list must pass the test
20-
if (input.is_array())
21-
{
22-
for (auto i : input)
23-
if (!verify_json(pointer, i, rules))
24-
return false;
25-
return true;
26-
}
27-
2818
// Find all rules that apply for the input node
2919
// TODO: accelerate this
30-
std::vector<json> matching_rules;
31-
for (auto i : rules)
32-
{
33-
if (i.at("pointer") == pointer)
34-
matching_rules.push_back(i);
35-
}
20+
std::vector<json> matching_rules = collect_pointer(pointer, rules);
3621

3722
// There must be at least one, otherwise warning and return true if not strict
3823
if (matching_rules.empty())
@@ -52,6 +37,16 @@ namespace sjv
5237

5338
if (count == 0 && !matching_rules.empty())
5439
{
40+
// Before giving up, try boxing a primitive type
41+
if (boxing_primitive && !input.is_array())
42+
{
43+
string new_pointer = (pointer == "/" ? "" : pointer) + "/*";
44+
// Make sure there are some rules for the boxed version before recursively checking
45+
if (collect_pointer(new_pointer, rules).size() > 0)
46+
if (verify_json(new_pointer, input, rules))
47+
return true;
48+
}
49+
5550
std::stringstream s;
5651
s << "No valid rules in this list:";
5752
for (auto i : matching_rules)
@@ -71,52 +66,62 @@ namespace sjv
7166
}
7267

7368
// If it passes and if it is a dictionary, then test all childrens
74-
if (input.is_structured())
69+
if (input.is_object())
7570
for (auto &i : input.items())
7671
{
77-
string new_pointer = pointer + i.key() + "/";
72+
string new_pointer = (pointer == "/" ? "" : pointer) + "/" + i.key();
7873
// first of all, let's check if the specs are correct
79-
json defaults = collect_default_rules(new_pointer,rules);
74+
json defaults = collect_default_rules(new_pointer, rules);
8075

8176
// if it is mandatory, make sure there are no defaults
82-
if (matching_rules[0].contains("required") && contained_in_list(i.key(),matching_rules[0]["required"]))
77+
if (matching_rules[0].contains("required") && contained_in_list(i.key(), matching_rules[0]["required"]))
8378
{
8479
if (defaults.size() != 0)
8580
{
86-
log.push_back(log_item("error","Inconsistent specifications: " + new_pointer + " is a mandatory field with a default value."));
81+
log.push_back(log_item("error", "Inconsistent specifications: " + new_pointer + " is a mandatory field with a default value."));
8782
return false;
8883
}
8984
}
9085
// if it is optional, there should be only one default in the specs
91-
else if (matching_rules[0].contains("optional") && contained_in_list(i.key(),matching_rules[0]["optional"]))
86+
else if (matching_rules[0].contains("optional") && contained_in_list(i.key(), matching_rules[0]["optional"]))
9287
{
9388
if (defaults.size() != 1)
9489
{
95-
log.push_back(log_item("error","Inconsistent specifications: " + new_pointer + " is an optional field with " + std::to_string(defaults.size()) + " default values."));
90+
log.push_back(log_item("error", "Inconsistent specifications: " + new_pointer + " is an optional field with " + std::to_string(defaults.size()) + " default values."));
9691
return false;
9792
}
9893
}
9994
// if it is not mandatory and not optional, something is wrong
10095
else
10196
{
102-
log.push_back(log_item("warning","Inconsistent specifications: " + new_pointer + " is neither an optional or a mandatory field."));
97+
log.push_back(log_item("warning", "Inconsistent specifications: " + new_pointer + " is neither an optional or a mandatory field."));
10398
if (strict)
10499
return false;
105100
}
106101

107102
// now let's make sure it can be validated
108103
if (!verify_json(new_pointer, i.value(), rules))
109104
return false;
110-
111105
}
112106

107+
// In case of a list
108+
// All the elements in the list must pass the test
109+
if (input.is_array())
110+
{
111+
for (auto i : input)
112+
if (!verify_json((pointer == "/" ? "" : pointer) + "/*", i, rules))
113+
return false;
114+
}
115+
113116
// If they all pass, return true
114117
return true;
115118
};
116119
bool SJV::verify_rule(const json &input, const json &rule)
117120
{
118121
string type = rule.at("type");
119-
if (type == "float")
122+
if (type == "list")
123+
return verify_rule_list(input, rule);
124+
else if (type == "float")
120125
return verify_rule_float(input, rule);
121126
else if (type == "int")
122127
return verify_rule_int(input, rule);
@@ -152,7 +157,7 @@ namespace sjv
152157
if (strict)
153158
return false;
154159
}
155-
160+
156161
if (rule.contains("extensions"))
157162
{
158163
std::string ext = p.extension();
@@ -179,7 +184,7 @@ namespace sjv
179184
if (strict)
180185
return false;
181186
}
182-
187+
183188
return true;
184189
};
185190
bool SJV::verify_rule_float(const json &input, const json &rule)
@@ -255,6 +260,22 @@ namespace sjv
255260
return true;
256261
};
257262

263+
bool SJV::verify_rule_list(const json &input, const json &rule)
264+
{
265+
assert(rule.at("type") == "list");
266+
267+
if (!input.is_array())
268+
return false;
269+
270+
if (rule.contains("min") && !(input.size() >= rule["min"]))
271+
return false;
272+
273+
if (rule.contains("max") && !(input.size() <= rule["max"]))
274+
return false;
275+
276+
return true;
277+
}
278+
258279
std::string SJV::log2str()
259280
{
260281
std::stringstream s;
@@ -265,7 +286,7 @@ namespace sjv
265286
}
266287
return s.str();
267288
};
268-
289+
269290
json SJV::collect_default_rules(const string &pointer, const json &rules)
270291
{
271292
// Find all rules that apply for the input node
@@ -281,8 +302,19 @@ namespace sjv
281302
return matching_rules;
282303
};
283304

284-
bool SJV::contained_in_list(string item, const json& list)
305+
bool SJV::contained_in_list(string item, const json &list)
306+
{
307+
return std::find(list.begin(), list.end(), item) != list.end();
308+
};
309+
310+
std::vector<json> SJV::collect_pointer(const string& pointer, const json& rules)
285311
{
286-
return std::find(list.begin(),list.end(),item) != list.end();
312+
std::vector<json> matching_rules;
313+
for (auto i : rules)
314+
{
315+
if (i.at("pointer") == pointer)
316+
matching_rules.push_back(i);
317+
}
318+
return matching_rules;
287319
};
288320
} // namespace sjv

src/sjv/sjv.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ 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
18+
// Verify a node pointed by
1919
bool verify_json(const string &pointer, const json &input, const json &rules);
2020

2121
// Dispatcher for rule verification
@@ -29,9 +29,7 @@ namespace sjv
2929
bool verify_rule_string(const json &input, const json &rule);
3030
bool verify_rule_object(const json &input, const json &rule);
3131
bool verify_rule_bool(const json &input, const json &rule);
32-
33-
// Collect all rules having a default for a given pointer
34-
json collect_default_rules(const string &pointer, const json &rules);
32+
bool verify_rule_list(const json &input, const json &rule);
3533

3634
// TODO
3735
json generate_default_json(const json &rules);
@@ -42,15 +40,25 @@ namespace sjv
4240
// if strict == false, a json is valid even if it has entries not validated by a rule
4341
bool strict = false;
4442

43+
// automatic boxing for primitive types
44+
// if all rules fail for a basic type, try boxing it once and try again
45+
bool boxing_primitive = true;
46+
4547
// message list
46-
typedef std::pair<std::string,std::string> log_item;
48+
typedef std::pair<std::string, std::string> log_item;
4749
std::vector<log_item> log;
4850

4951
// log to string
5052
std::string log2str();
5153

54+
// Collect all rules having a default for a given pointer
55+
json collect_default_rules(const string &pointer, const json &rules);
56+
57+
// Collect all rules having a given pointer
58+
std::vector<json> collect_pointer(const string &pointer, const json &rules);
59+
5260
// Utils
53-
bool contained_in_list(string item, const json& list);
61+
bool contained_in_list(string item, const json &list);
5462
};
5563

5664
} // namespace sjv

0 commit comments

Comments
 (0)