Schema based JSON Parser/Serializer
Project description
jsonbp
jsonbp (JSON BluePrint) is a library to validate and deserialize JSON into Python data based on a schema. There is json-schema and its implementations already if you want a more mature and more widely used technique, but I needed some features that were not easily available by just patching json-schema, so this library is the result.
jsonbp's design main goals were:
- schema reuse through import / type system
- support for enums
- built in numeric fixed precision type (which deserializes directly into Python's Decimal)
- built in datetime type (which deserializes directly into Python's datetime)
- error reporting with support for localization
- easy to integrate and use
Contents
Schema definition
jsonbp uses its own (very simple) domain language to define a schema. The only mandatory declaration is the "root" entry, which determines the expected JSON contents. Here's a simple hypothetical example:
root {
x: float (min=0.0),
y: float (min=0.0, max=1.0),
optional color: {
RED,
GOLD,
GREEN
}
} [minLength=2]
This defines a schema that represents an array of minimum length 2 whose elements contain the fields 'x' and 'y' (where both 'x' and 'y' are not allowed to be negative and 'y' is not allowed to be greater than 1.0) and an optional field 'color' that can be either "RED", "GOLD" or "GREEN". A JSON instance that obeys this schema is:
[
{
"x": 2.0,
"y", 0.004,
"color": "RED"
},
{
"x": 3.0,
"y", 0.009
},
{
"x": 4.0,
"y", 0.016,
"color": "GREEN"
}
]
Besides "root", in jsonbp the following optional directives can be used to organize a schema:
- "type" -> to define specialized (restricted) simple types
- "node" -> to specify the contents of compound types (Objetcs)
- "enum" -> to define a list of allowed values for given fields
- "import" -> to reuse directives from existing blueprints
One can then make use of these features to simplify and make the schema more modular. In the above example schema, we could split the definitions and get something more reusable, like the following:
type non_negative : float (min=0.0)
type normalized : non_negative (max=1.0)
node coordinates {
x: non_negative,
y: normalized
}
include "color.jbp"
node colored_coordinates extends coordinates {
optional color: color
}
root colored_coordinates[minLength=2]
where the contents of file "color.jbp" would then be:
enum color {
RED,
GOLD,
GREEN
}
(For detailed information using these directives, see the Documentation section)
Usage
All of jsonbp can be summarized in 2 functionalities:
Schema parsing
- jsonbp.load(<schema file path>) => <blueprint object>
- jsonbp.loads(<schema string>) => <blueprint object>
These functions are used for schema/blueprint loading.
Both do the same thing, the only difference is that the former expects a path to a file
storing the schema content while the latter expects a string with the schema content itself.
When there's a problem with the supplied schema, an exception is thrown. More on this can
be read on Error handling and error localization
. These functions, when
succeed loading the schema, return a blueprint instance that can then be used to deserialize
JSON strings.
When loading a file, the blueprint is stored in a cache and associated with the absolute path of that file. Thus, loading the same file twice won't make jsonbp parse it a second time. To force the parsing of posterior calls to load(), call jsonbp.invalidateCache().
JSON deserialization
- <blueprint object>.deserialize(<JSON string>) => (success, outcome)
This is the only method that is intended to be invoked from the blueprint object returned by load()/loads(). It expects a string holding the JSON contents to be deserialized. It returns a tuple in the form (success, outcome). success being a boolean signalizing if the deserialization was successful or not. If successful, outcome will hold the Python data obtained from the JSON string. Otherwise (success is false) outcome will have a message explaining what was not compliant according to the schema.
Example
Here's how one uses these functions in code:
import jsonbp
blueprint = jsonbp.loads('''
root {
success: {
YES,
NO
}
}
''')
jsonInstance = '{ "success": "YES" }'
success, outcome = blueprint.deserialize(jsonInstance)
print(f'Success: {success}')
print(f'Outcome: {outcome}')
Requirements and Dependencies
jsonbp requires Python 3.6+, that's it.
Under the hood, jsonbp uses PLY for its schema parsing. PLY comes
included with jsonbp already, there's no need to download it separately.
Installation
jsonbp is available at PyPI: https://pypi.org/project/jsonbp/
To install through pip:
pip install jsonbp
Documentation
That was just an introduction, if you are interested in using jsonbp here are some more detailed information:
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.