An extension of JSON with an emphasis on reusability
Installation (requires pip)
pip install jpp
usage: cli.py [-h] [--version] [-p PATH] [-c] [-u USER_INPUT] [file]
positional arguments:
file Path to main JSON++ file
optional arguments:
-h, --help show this help message and exit
--version show program's version number and exit
-p PATH, --path PATH One or more path to add to JSON++ path
-c, --compact-print If specified, will print the most compact version
-u USER_INPUT, --user-input USER_INPUT
Optional user input values
Write to stdout the generation of a json based on the given jpp file, ex.jpp
:
jpp ex.jpp
--path
option allows users to add specific locations to JPP_PATH
if the --compact-print
option is given, the result JSON is printed in the most compact fashion.
--user_input
allows users to pass input, in JSON format, via CLI. See User input
Features (see unit-tests for more)
Perhaps an example is the best way to show the supported features of JSON++. Assuming the following is the contents of ex.jpp
:
# Comments support
# Referencing values in other files
import other;
# All data besides imports must be inside the global scope
{
# Any standard JSON will do
"standard json": {
"hello": "world",
"list": [1, 2, 3],
"number": 3.14,
},
# Referencing other local values
"local ref example": {
"some val": 8,
"same val": local["local ref example"]["some val"], # evaluates "some val" to 8
"nested": {
"some val": 17
},
"nested_ref": local["local ref example"]["nested"]["some val"] + 3 # evaluates to 20
},
"remote import example": {
"value from other file": imported["other"]["some val"]
},
# Dictionary extending
"base dict": {
"foo": 3
},
# "sub dict" has 2 entries: "foo": 3 and "bar": 12
"sub dict" extends local["base dict"]: {
"bar": 12
}
}
... and assuming other.jpp
contains the following:
{
"some val": 123
}
Then executing jpp ex.jpp
will print the following to the standard output:
{
"sub dict": {
"foo": 3,
"bar": 12
},
"base dict": {
"foo": 3
},
"remote import example": {
"value from other file": 123
},
"local ref example": {
"nested_ref": 20,
"nested": {
"some val": 17
},
"same val": 8,
"some val": 8
},
"standard json": {
"number": 3.14,
"list": [
1,
2,
3
],
"hello": "world"
}
}
As can be seen, the printed result is a valid JSON with all of references and calculated values being replaced.
What happens when a jpp
file encounters an import statement, e.g., import foo.bar.baz;
?
The parser will look for a file foo.bar.jpp
in it's path.
Similar to PYTHONPATH, JPP_PATH
is an environment variable which contains paths separated by ;
. By default, when the CLI is executed, the current directory is added to the path. Additionally, the user may add additional locations by using the path
option when invoking the CLI.
E.g., consider the following commands being executed from a shell:
$> export JPP_PATH=~/foobar
$> cd ~/foo/bar
$> jpp --path ["~/foo/baz", "~/qux/quux"] example.jpp
When jpp
is run, JPP_PATH
looks like this:
JPP_PATH=~/foo/bar;~/foo/baz;~/qux/quux;~/foobar
Notice that the search is according to the order of which the locations are specified. So, for example, if example.jpp
contains the statement import corge.grault;
then ~/foo/bar
is searched first, and only if not found does the parser search ~/foo/baz
and the rest, etc.
Sometimes certain values aren't know until runtime. For these kinds of situations JSON++ allows referencing user-given values. This is done by specifying the --user_input
option, and using the user_input
keyword in the your jpp
document. E.g., assuming example.jpp
contains the following:
{
"version": user_input["version"],
"cool feature supported": local["version"] >= "2.0"
}
Executing jpp example.jpp -u '{"version": "2.3.1"}'
will give:
{
"version": "2.3.1",
"cool feature supported": true
}
Executing jpp example.jpp -u '{"version": "1.9"}'
will give:
{
"version": "1.9",
"cool feature supported": false
}