JSON value validator
Project description
JSONvv
JSON value validator
Overview
This is a simple JSON schema validator library. It was created for Camoufox to validate passed user configurations. Because I found it useful for other projects, I decided to extract it into a separate library.
JSONvv's syntax parser is written in pure Python. It does not rely on any dependencies.
Example
| Configuration | Validator |
|---|---|
config = {
"username": "johndoe",
"email": "johndoe@example.com",
"age": 30,
"chat": "Hello world!",
"preferences": {
"notifications": True,
"theme": "dark"
},
"allowed_commands": [
"/help", "/time", "/weather"
],
"location": [40.7128, -74.0060],
"hobbies": [
{
"name": "Traveling",
"cities": ["Paris", "London"]
},
{
"name": "reading",
"hours": {
"Sunday": 2,
"Monday": 3,
}
}
]
}
|
validator = {
"username": "str", # Basic username
"email": "str[/\S+@\S+\.\S+/]", # Validate emails
"age": "int[>=18]", # Age must be 18 or older
"chat": "str | nil", # Optional chat message
"preferences": {
"notifications": "bool",
"theme": "str[light, dark] | nil", # Optional theme
},
# Commands must start with "/", but not contain "sudo"
"allowed_commands": "array[str[/^//] - str[/sudo/]]",
# Validate coordinate ranges
"location": "tuple[double[-90 - 90], double[-180 - 180]]",
# Handle an array of hobby types
"hobbies": "array[@traveling | @other, >=1]",
"@traveling": {
# Require 1 or more cities/countries iff name is "Traveling"
"*name,type": "str[Traveling]",
"*cities,countries": "array[str[A-Za-z*], >=1]",
},
"@other": {
"name,type": "str - str[Traveling]", # Non-traveling types
# If hour(s) is specified, require days have >0 hours
"/hours?/": {
"*/day$/": "int[>0]"
}
}
}
|
Then, validate the configuration like this:
from jsonvv import JsonValidator, JvvRuntimeException
val = JsonValidator(validator)
try:
val.validate(config)
except JvvRuntimeException as exc:
print("Failed:", exc)
else:
print('Config is valid!')
Table of Contents
Keys Syntax
Dictionary keys can be specified in several possible ways:
"key": "type""key1,key2,key3": "type""/key\d+/": "type""*required_key": "type"
Regex patterns
To use regex in a key, wrap it in / ... /.
Syntax:
"/key\d+/": "type"
Lists of possible values
To specify a list of keys, use a comma-separated string.
Syntax:
"key1,key2,key3": "type"
"/k[ey]{2}1/,key2": "type"
To escape a comma, use !.
Required fields (*)
Fields marked with * are required. The validation will fail without them.
Syntax:
"*key1": "type"
"*/key\d+/": "type"
Grouping keys ($)
Fields that end with $group_name are grouped together. If one of the keys is set, all of the keys in the group must also be set as well.
Syntax:
"isEnabled$group1": "bool"
"value$group1": "int[>0]"
This will require both value is set if and only if isEnabled is set.
Multiple $ can be used to create more complex group dependencies.
keyOne$group1: "any"
keyTwo$group2: "any"
keyThree$group1$group2: "any"
If keyThree is set, both keyOne and keyTwo must also be set.
However, if keyOne is set, keyThree needs to be set but keyTwo does not.
Supported Types
String (str)
Represents a string value. Optionally, you can specify a regex pattern that the string must match.
Syntax:
- Basic string:
"str" - With regex pattern:
"str[regex_pattern]" - The escape character for regex is
\, and for commas is_.
Arguments:
regex_pattern: A regular expression that the string must match. If not specified, any string is accepted.
Examples:
-
Basic string:
"username": "str"
Accepts any string value for the key
username. -
String with regex pattern:
"fullname": "str[/[A-Z][a-z]+ [A-Z][a-z]+/]"
Accepts a string that matches the pattern of a first and last name starting with uppercase letters.
Integer (int)
Represents an integer value. You can specify conditions like exact values, ranges, and inequalities.
Syntax:
- Basic integer:
"int" - With conditions:
"int[conditions]"
Arguments:
conditions: A comma-separated list of conditions.
Condition Operators:
==: Equal to a specific value.>=: Greater than or equal to a value.<=: Less than or equal to a value.>: Greater than a value.<: Less than a value.range: A range between two values (inclusive).
Examples:
-
Basic integer:
"age": "int"
Accepts any integer value for the key
age. -
Integer with conditions:
"userage": "int[>=0, <=120]"
Accepts integer values between 0 and 120 inclusive.
-
Specific values and ranges
"rating": "int[1-5]" "rating": "int[1,2,3,4-5]"
Accepts integer values 1, 2, 3, 4, or 5.
-
Ranges with negative numbers:
"rating": "int[-100 - -90]"
Accepts integer values from -100 to -90.
Double (double)
Represents a floating-point number. Supports the same conditions as integers.
Syntax:
- Basic double:
"double" - With conditions:
"double[conditions]"
Arguments:
conditions: A comma-separated list of conditions.
Examples:
-
Basic double:
"price": "double"
Accepts any floating-point number for the key
price. -
Double with conditions:
"percentage": "double[>=0.0,<=100.0]"
Accepts double values between 0.0 and 100.0 inclusive.
Boolean (bool)
Represents a boolean value (True or False).
Syntax:
"isActive": "bool"
Accepts a boolean value for the key isActive.
Array (array)
Represents a list of elements of a specified type. You can specify conditions on the length of the array.
Syntax:
- Basic array:
"array[element_type]" - With length conditions:
"array[element_type,length_conditions]"
Arguments:
element_type: The type of the elements in the array.length_conditions: Conditions on the array length (same as integer conditions).
Examples:
-
Basic array:
"tags": "array[str]"
Accepts a list of strings for the key
tags. -
Array with length conditions:
"scores": "array[int[>=0,<=100],>=1,<=5]"
Accepts a list of 1 to 5 integers between 0 and 100 inclusive.
-
Fixed-length array:
"coordinates": "array[double, 2]"
Accepts a list of exactly 2 double values.
-
More complex restraints:
"coordinates": "array[array[int[>0]] - tuple[1, 1]], 2]"
Tuple (tuple)
Represents a fixed-size sequence of elements of specified types.
Syntax:
"tuple[element_type1, element_type2]"
Arguments:
element_typeN: The type of the Nth element in the tuple.
Examples:
-
Basic tuple:
"point": "tuple[int, int]"
Accepts a tuple or list of two integers.
-
Tuple with mixed types:
"userInfo": "tuple[str, int, bool]"
Accepts a tuple of a string, an integer, and a boolean.
Nested Dictionaries
Represents a nested dictionary structure. Dictionaries are defined using Python's dictionary syntax {} in the type definitions.
Syntax:
"settings": {
"volume": "int[>=0,<=100]",
"brightness": "int[>=0,<=100]",
"mode": "str"
}
Usage:
- Define the expected keys and their types within the dictionary.
- You can use all the supported types for the values.
Examples:
-
Nested dictionary:
"user": { "name": "str", "age": "int[>=0]", "preferences": { "theme": "str", "notifications": "bool" } }
Defines a nested dictionary structure for the key
user.
Nil (nil)
Represents a None value.
Syntax:
"optionalValue": "int | nil"
Usage:
- Use
nilto allow a value to beNone. - Often used with union types to specify optional values.
Any (any)
Represents any value.
Syntax:
"metadata": "any"
Usage:
- Use
anywhen any value is acceptable. - Useful for keys where the value is not constrained.
Type References (@)
Allows you to define reusable types and reference them.
Syntax:
-
Define a named type:
"@typeName": "type_definition"
-
Reference a named type:
"key": "@typeName"
Examples:
-
Defining and using a named type:
"@positiveInt": "int[>0]" "userId": "@positiveInt"
Defines a reusable type
@positiveIntand uses it for the keyuserId.
Advanced Features
Subtracting Domains (-)
Allows you to specify that a value should not match a certain type or condition.
Syntax:
"typeA - typeB"
Usage:
- The value must match
typeAbut nottypeB.
Examples:
-
Excluding certain strings:
"message": "str - str[.*error.*]"
Accepts any string that does not match the regex pattern
.*error.*. -
Excluding a range of numbers:
"score": "int[0-100] - int[>=90]"
Accepts integers between 0 and 100, excluding values greater than or equal to 90.
-
Excluding multiple types:
"score": "int[>0,<100] - int[>90] - int[<10]" # Union, then subtraction: "score": "int[>0,<100] - int[>90] | int[<10]" "score": "int[>0,<100] - (int[>90] | int[<10])" # same thing # Use parenthesis to run subtraction first "score": "int[>0,<50] | (int[<100] - int[<10])" "score": "(int[<100] - int[<10]) | int[>0,<50]"
Note: Union is handled before subtraction.
-
Allowing all but a specific value:
"specialNumber": "any - int[0]"
Union Types (|)
Allows you to specify that a value can be one of multiple types.
Syntax:
"typeA | typeB | typeC"
Usage:
- The value must match at least one of the specified types.
Examples:
-
Multiple possible types:
"data": "int | str | bool"
Accepts an integer, string, or boolean value for the key
data. -
Combining with arrays:
"mixedList": "array[int | str]"
Accepts a list of integers or strings.
Conditional Ranges and Values
Specifies conditions that values must satisfy, including ranges and specific values.
Syntax:
- Greater than:
">value" - Less than:
"<value" - Greater than or equal to:
">=value" - Less than or equal to:
"<="value" - Range:
"start-end" - Specific values:
"value1,value2,value3"
Examples:
-
Integer conditions:
"level": "int[>=1,<=10]"
Accepts integers from 1 to 10 inclusive.
-
Double with range:
"latitude": "double[-90.0 - 90.0]"
Accepts doubles between -90.0 and 90.0 inclusive.
-
Specific values:
"status": "int[1,2,3]"
Accepts integers that are either 1, 2, or 3.
Error Handling
graph TD
Exception --> JvvException
JvvException --> JvvRuntimeException
JvvException --> JvvSyntaxError
JvvRuntimeException --> UnknownProperty["UnknownProperty<br/><small>Raised when a key in config<br/>isn't defined in property types</small>"]
JvvRuntimeException --> InvalidPropertyType["InvalidPropertyType<br/><small>Raised when a value doesn't<br/>match its type definition</small>"]
InvalidPropertyType --> MissingRequiredKey["MissingRequiredKey<br/><small>Raised when a required key<br/>is missing from config</small>"]
MissingRequiredKey --> MissingGroupKey["MissingGroupKey<br/><small>Raised when some keys in a<br/>property group are missing</small>"]
JvvSyntaxError --> PropertySyntaxError["PropertySyntaxError<br/><small>Raised when property type<br/>definitions have syntax errors</small>"]
classDef base fill:#eee,stroke:#333,stroke-width:2px;
classDef jvv fill:#d4e6f1,stroke:#2874a6,stroke-width:2px;
classDef runtime fill:#d5f5e3,stroke:#196f3d,stroke-width:2px;
classDef syntax fill:#fdebd0,stroke:#b9770e,stroke-width:2px;
classDef error fill:#fadbd8,stroke:#943126,stroke-width:2px;
class Exception base;
class JvvException jvv;
class JvvRuntimeException,JvvSyntaxError runtime;
class PropertySyntaxError syntax;
class UnknownProperty,InvalidPropertyType,MissingRequiredKey,MissingGroupKey error;
Types
-
str: Basic string type.
- Arguments:
regex_pattern(optional): A regex pattern the string must match.
- Example:
"str[^[A-Za-z]+$]"
- Arguments:
-
int: Integer type with conditions.
- Arguments:
conditions: Inequalities (>=,<=,>,<), specific values (value1,value2), ranges (start-end).
- Example:
"int[>=0,<=100]"
- Arguments:
-
double: Double (floating-point) type with conditions.
- Arguments:
- Same as
int.
- Same as
- Example:
"double[>0.0]"
- Arguments:
-
bool: Boolean type.
- Arguments: None.
- Example:
"bool"
-
array: Array (list) of elements of a specified type.
- Arguments:
element_type: Type of elements in the array.length_conditions(optional): Conditions on the array length.
- Example:
"array[int[>=0],>=1,<=10]"
- Arguments:
-
tuple: Fixed-size sequence of elements of specified types.
- Arguments:
- List of element types.
- Example:
"tuple[str, int, bool]"
- Arguments:
-
nil: Represents a
Nonevalue.- Arguments: None.
- Example:
"nil"
-
any: Accepts any value.
- Arguments: None.
- Example:
"any"
-
Type References: Reusable type definitions.
- Arguments:
@typeName: Reference to a named type.
- Example:
- Define:
"@positiveInt": "int[>0]" - Use:
"userId": "@positiveInt"
- Define:
- Arguments:
Type Combinations
-
Union Types (
|): Value must match one of multiple types.- Syntax:
"typeA | typeB" - Example:
"str | int"
- Syntax:
-
Subtracting Domains (
-): Value must matchtypeAbut nottypeB.- Syntax:
"typeA - typeB" - Example:
"int - int[13]"(any integer except 13)
- Syntax:
Escaping Characters
!: Escapes commas, slashes, and other jsonvv characters within strings.\: Escapes within a regex pattern.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jsonvv-0.2.1.tar.gz.
File metadata
- Download URL: jsonvv-0.2.1.tar.gz
- Upload date:
- Size: 17.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
662402c875068792767017b2e5d9dce22c8cb4e769708426bf1f5c23e8a47878
|
|
| MD5 |
3a9f9081fe7e4da6070109433ee03d71
|
|
| BLAKE2b-256 |
5bf7ed0a937a1820bc7a07f4bd1d063968b52a3e17a14f372e839c19283d1047
|
File details
Details for the file jsonvv-0.2.1-py3-none-any.whl.
File metadata
- Download URL: jsonvv-0.2.1-py3-none-any.whl
- Upload date:
- Size: 15.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9b1dd5cfdc4ea810c660e6a33c624bef2cb12ed2f018dfb0008375a6c87518a4
|
|
| MD5 |
28cd4e748c44c18944475eaf2abbcb18
|
|
| BLAKE2b-256 |
156f4b1750758d75605caeff3a3c0caf5242cadd48107726a214482e820bd1ed
|