Skip to content

Commit 571a02d

Browse files
author
teseo@sonic
committed
2 parents 0a0a352 + cfe5f02 commit 571a02d

File tree

2 files changed

+171
-4
lines changed

2 files changed

+171
-4
lines changed

README.md

Lines changed: 165 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,169 @@
11
[![Build](https://github.com/geometryprocessing/json-spec-engine/actions/workflows/continuous.yml/badge.svg?branch=main)](https://github.com/geometryprocessing/json-spec-engine/actions/workflows/continuous.yml)
22

3+
# Json Spec Engine
4+
5+
JSE is a library to validate a json file containing a set of parameters and to complete it with default parameters. The specification of the json format is itself a json file. The library contains python scripts for generating documentation for the json format in html format and as a graph.
6+
7+
## Example
8+
9+
Given a json file as input:
10+
```cpp
11+
json input = R"(
12+
{
13+
"string1": "teststring",
14+
"geometry":
15+
{
16+
"nested": 3
17+
}
18+
}
19+
)"_json;
20+
```
21+
and a set of rules to define its format
22+
```cpp
23+
json rules = R"(
24+
[
25+
{
26+
"pointer": "/",
27+
"type": "object",
28+
"required": ["string1"],
29+
"optional":["geometry","other"]
30+
},
31+
{
32+
"pointer": "/string1",
33+
"type": "string"
34+
},
35+
{
36+
"pointer": "/geometry",
37+
"type": "object",
38+
"default": null,
39+
"optional": ["nested"]
40+
},
41+
{
42+
"pointer": "/geometry/nested",
43+
"type": "int",
44+
"default": 3
45+
},
46+
{
47+
"pointer": "/other",
48+
"type": "object",
49+
"optional": ["nested"],
50+
"default": null
51+
},
52+
{
53+
"pointer": "/other/nested",
54+
"type": "int",
55+
"default": 3
56+
}
57+
]
58+
)"_json;
59+
```
60+
The library can be used to test the validity of the json file:
61+
```cpp
62+
JSE jse;
63+
64+
bool r = jse.verify_json(input, rules);
65+
std::string s = jse.log2str();
66+
```
67+
any eventual error is reported in s.
68+
69+
Additionally, the library can automatically complete the json with the default parameters in the specification:
70+
```cpp
71+
json return_json = jse.inject_defaults(input, rules);
72+
```
73+
generating the following json
74+
```cpp
75+
json output = R"(
76+
{
77+
"string1": "teststring",
78+
"geometry":
79+
{
80+
"nested": 3
81+
},
82+
"other":
83+
{
84+
"nested": 3
85+
}
86+
}
87+
)"_json;
88+
```
89+
90+
## Rules
91+
92+
Every rule must have a property called "pointer", that refers to an entry in the input, and a type, that specified the type of the entry. A list in the hierarchy is represented with a * character.
93+
94+
For a json to be file to be valid, every entry in the json must match *one and only one* rule. Each rule matches an input depending on its type and on rule-specific parameters.
95+
96+
The specification supports polymorphic types: it is possible to specify multiple rules for the same pointer. In this case the file is valid as long as any of the rules is valid.
97+
98+
### type: object
99+
100+
The entry must be an object. The object can contain a set of required fields (required), and a set of optional fields. Note that every optional field must provide a default value in the spec. Additionally, it is possible to specify a type-name, in that case the object must contain a field called type with the matching name.
101+
102+
Optional parameters:
103+
* required: list of fields required for this object to be valid
104+
* optional: list of fields that are optional. Every optional field must have at least one rule with a default
105+
* type-name: the object must have a type field with the same name
106+
107+
### "type": "float"
108+
109+
The entry must be a floating point number, and optionally within a given range.
110+
111+
Optional parameters:
112+
* min: number
113+
* max: number
114+
115+
### "type": "int"
116+
117+
The entry must be an integer number, and optionally within a given range.
118+
119+
Optional parameters:
120+
* min: number
121+
* max: number
122+
123+
### "type": "file"
124+
125+
The entry must contain a valid path to a file that exists on the filesystem. Optionally, it is possible to specify valid extensions as a list of strings.
126+
127+
Optional parameters:
128+
* extensions: [".txt",".msh"]
129+
130+
### "type": "folder"
131+
132+
The entry must contain a valid path to a folder that exists on the filesystem.
133+
134+
### "type": "string"
135+
136+
The entry must be a string. Optionally, it is possible to restrict the valid strings to a set of predefined ones.
137+
138+
Optional parameters:
139+
* options: ["these","are","options"]
140+
141+
### "type": "list"
142+
143+
The entry must be a list. Optionally, it is possible to restrict the lenght of the list.
144+
145+
Optional parameters:
146+
* min: minimal size
147+
* max: maximal size
148+
149+
### "type": "bool"
150+
151+
The entry must be a boolean value.
152+
153+
## Defaults
154+
155+
Every rule associated with an optional parameter of an object must specify a default value, using the default field.
156+
157+
# Advanced options
158+
```cpp
159+
jse.strict // DEFAULT: false - if strict == false, a json is valid even if it has entries not validated by a rule
160+
jse.skip_file_check // DEFAULT: true - disable checking for existance of file and folders
161+
jse.boxing_primitive // DEFAULT: true - always try to convert any type t to a list of t for the purpose of finding a valid rule
162+
```
163+
164+
## Authors
165+
Zachary Ferguson,
166+
Teseo Schneider,
167+
Daniele Panozzo
3168

4-
1. write a function to complete fields with default
5-
2. write a function to find all children of a pointer
6-
3. vector type
7169

scripts/graphviz.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
with open('data/default_rules.json')as fp:
99
rules = json.load(fp)
1010

11+
def getter(contain, key):
12+
if key not in contain:
13+
return []
14+
return contain[key]
15+
1116
def parse_tree(rules, mark_required = False):
1217
tree = dict()
1318
for r in rules:
@@ -41,7 +46,7 @@ def rename(node):
4146
continue
4247
if k in rename.used_names:
4348
rename.used_names[k] += 1
44-
node[k+' '*(used_names[k])] = v
49+
node[k+' '*(rename.used_names[k])] = v
4550
node.pop(k)
4651
else:
4752
rename.used_names[k] = 0

0 commit comments

Comments
 (0)