# 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
<table>
<tr>
<th width="50%">Configuration</th>
<th width="50%">Validator</th>
</tr>
<tr>
<td width="50%">
```python
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,
}
}
]
}
```
</td>
<td width="50%">
```python
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]"
}
}
}
```
</td>
</tr>
</table>
<hr width=50>
Then, validate the configuration like this:
```python
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
- [Key Syntax](#key-syntax)
- [Regex patterns](#regex-patterns)
- [Lists of possible values](#lists-of-possible-values)
- [Required fields (`*`)](#required-fields-)
- [Grouping keys (`$`)](#grouping-keys-)
- [Supported Types](#supported-types)
- [String (`str`)](#string-str)
- [Integer (`int`)](#integer-int)
- [Double (`double`)](#double-double)
- [Boolean (`bool`)](#boolean-bool)
- [Array (`array`)](#array-array)
- [Tuple (`tuple`)](#tuple-tuple)
- [Nested Dictionaries](#nested-dictionaries)
- [Nil (`nil`)](#nil-nil)
- [Any (`any`)](#any-any)
- [Required fields (`*`)](#required-fields-)
- [Type References (`@`)](#type-references-)
- [Advanced Features](#advanced-features)
- [Subtracting Domains (`-`)](#subtracting-domains--)
- [Union Types (`|`)](#union-types-)
- [Conditional Ranges and Values](#conditional-ranges-and-values)
- [Error Handling](#error-handling)
---
## 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:**
```python
"/key\d+/": "type"
```
### Lists of possible values
To specify a list of keys, use a comma-separated string.
**Syntax:**
```python
"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:**
```python
"*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:**
```python
"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.
---
## 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:**
1. Basic string:
```python
"username": "str"
```
Accepts any string value for the key `username`.
2. String with regex pattern:
```python
"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:**
1. Basic integer:
```python
"age": "int"
```
Accepts any integer value for the key `age`.
2. Integer with conditions:
```python
"userage": "int[>=0, <=120]"
```
Accepts integer values between 0 and 120 inclusive.
3. Specific values and ranges
```python
"rating": "int[1-5]"
"rating": "int[1,2,3,4-5]"
```
Accepts integer values 1, 2, 3, 4, or 5.
4. Ranges with negative numbers:
```python
"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:**
1. Basic double:
```python
"price": "double"
```
Accepts any floating-point number for the key `price`.
2. Double with conditions:
```python
"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:**
```python
"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:**
1. Basic array:
```python
"tags": "array[str]"
```
Accepts a list of strings for the key `tags`.
2. Array with length conditions:
```python
"scores": "array[int[>=0,<=100],>=1,<=5]"
```
Accepts a list of 1 to 5 integers between 0 and 100 inclusive.
3. Fixed-length array:
```python
"coordinates": "array[double, 2]"
```
Accepts a list of exactly 2 double values.
4. More complex restraints:
```python
"coordinates": "array[array[int[>0]] - tuple[1, 1]], 2]"
```
### Tuple (`tuple`)
Represents a fixed-size sequence of elements of specified types.
**Syntax:**
```python
"tuple[element_type1, element_type2]"
```
**Arguments:**
- `element_typeN`: The type of the Nth element in the tuple.
**Examples:**
1. Basic tuple:
```python
"point": "tuple[int, int]"
```
Accepts a tuple or list of two integers.
2. Tuple with mixed types:
```python
"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:**
```python
"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:**
1. Nested dictionary:
```python
"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:**
```python
"optionalValue": "int | nil"
```
**Usage:**
- Use `nil` to allow a value to be `None`.
- Often used with union types to specify optional values.
### Any (`any`)
Represents any value.
**Syntax:**
```python
"metadata": "any"
```
**Usage:**
- Use `any` when 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:
```python
"@typeName": "type_definition"
```
- Reference a named type:
```python
"key": "@typeName"
```
**Examples:**
1. Defining and using a named type:
```python
"@positiveInt": "int[>0]"
"userId": "@positiveInt"
```
Defines a reusable type `@positiveInt` and uses it for the key `userId`.
---
## Advanced Features
### Subtracting Domains (`-`)
Allows you to specify that a value should not match a certain type or condition.
**Syntax:**
```python
"typeA - typeB"
```
**Usage:**
- The value must match `typeA` but not `typeB`.
**Examples:**
1. Excluding certain strings:
```python
"message": "str - str[.*error.*]"
```
Accepts any string that does not match the regex pattern `.*error.*`.
2. Excluding a range of numbers:
```python
"score": "int[0-100] - int[>=90]"
```
Accepts integers between 0 and 100, excluding values greater than or equal to 90.
3. Excluding multiple types:
```python
"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.
4. Allowing all but a specific value:
```python
"specialNumber": "any - int[0]"
```
### Union Types (`|`)
Allows you to specify that a value can be one of multiple types.
**Syntax:**
```python
"typeA | typeB | typeC"
```
**Usage:**
- The value must match at least one of the specified types.
**Examples:**
1. Multiple possible types:
```python
"data": "int | str | bool"
```
Accepts an integer, string, or boolean value for the key `data`.
2. Combining with arrays:
```python
"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:**
1. Integer conditions:
```python
"level": "int[>=1,<=10]"
```
Accepts integers from 1 to 10 inclusive.
2. Double with range:
```python
"latitude": "double[-90.0 - 90.0]"
```
Accepts doubles between -90.0 and 90.0 inclusive.
3. Specific values:
```python
"status": "int[1,2,3]"
```
Accepts integers that are either 1, 2, or 3.
---
## Error Handling
```mermaid
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]+$]"`
- **int**: Integer type with conditions.
- Arguments:
- `conditions`: Inequalities (`>=`, `<=`, `>`, `<`), specific values (`value1,value2`), ranges (`start-end`).
- Example: `"int[>=0,<=100]"`
- **double**: Double (floating-point) type with conditions.
- Arguments:
- Same as `int`.
- Example: `"double[>0.0]"`
- **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]"`
- **tuple**: Fixed-size sequence of elements of specified types.
- Arguments:
- List of element types.
- Example: `"tuple[str, int, bool]"`
- **nil**: Represents a `None` value.
- 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"`
### Type Combinations
- **Union Types** (`|`): Value must match one of multiple types.
- Syntax: `"typeA | typeB"`
- Example: `"str | int"`
- **Subtracting Domains** (`-`): Value must match `typeA` but not `typeB`.
- Syntax: `"typeA - typeB"`
- Example: `"int - int[13]"` (any integer except 13)
### Escaping Characters
- `!`: Escapes commas, slashes, and other jsonvv characters within strings.
- `\`: Escapes within a regex pattern.
Raw data
{
"_id": null,
"home_page": "https://github.com/daijro/camoufox/tree/main/pythonlib/jsonvv",
"name": "jsonvv",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.8",
"maintainer_email": null,
"keywords": "json, validator, validation, typing",
"author": "daijro",
"author_email": "daijro.dev@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/0a/01/d7a719321baf7a93d444054840d9ca17c4bd5ccde58cb7af7b7e620762c2/jsonvv-0.2.2.tar.gz",
"platform": null,
"description": "# JSONvv\n\nJSON value validator\n\n## Overview\n\nThis 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.\n\nJSONvv's syntax parser is written in pure Python. It does not rely on any dependencies.\n\n### Example\n\n<table>\n<tr>\n<th width=\"50%\">Configuration</th>\n<th width=\"50%\">Validator</th>\n</tr>\n<tr>\n<td width=\"50%\">\n\n```python\nconfig = {\n \"username\": \"johndoe\",\n \"email\": \"johndoe@example.com\",\n \"age\": 30,\n \"chat\": \"Hello world!\",\n \"preferences\": {\n \"notifications\": True,\n \"theme\": \"dark\"\n },\n \"allowed_commands\": [\n \"/help\", \"/time\", \"/weather\"\n ],\n \"location\": [40.7128, -74.0060],\n \"hobbies\": [\n {\n \"name\": \"Traveling\",\n \"cities\": [\"Paris\", \"London\"]\n },\n {\n \"name\": \"reading\",\n \"hours\": {\n \"Sunday\": 2,\n \"Monday\": 3,\n }\n }\n ]\n}\n```\n\n</td>\n<td width=\"50%\">\n\n```python\nvalidator = {\n \"username\": \"str\", # Basic username\n \"email\": \"str[/\\S+@\\S+\\.\\S+/]\", # Validate emails\n \"age\": \"int[>=18]\", # Age must be 18 or older\n \"chat\": \"str | nil\", # Optional chat message\n \"preferences\": {\n \"notifications\": \"bool\",\n \"theme\": \"str[light, dark] | nil\", # Optional theme\n },\n # Commands must start with \"/\", but not contain \"sudo\"\n \"allowed_commands\": \"array[str[/^//] - str[/sudo/]]\",\n # Validate coordinate ranges\n \"location\": \"tuple[double[-90 - 90], double[-180 - 180]]\",\n # Handle an array of hobby types\n \"hobbies\": \"array[@traveling | @other, >=1]\",\n \"@traveling\": {\n # Require 1 or more cities/countries iff name is \"Traveling\"\n \"*name,type\": \"str[Traveling]\",\n \"*cities,countries\": \"array[str[A-Za-z*], >=1]\",\n },\n \"@other\": {\n \"name,type\": \"str - str[Traveling]\", # Non-traveling types\n # If hour(s) is specified, require days have >0 hours\n \"/hours?/\": {\n \"*/day$/\": \"int[>0]\"\n }\n }\n}\n```\n\n</td>\n</tr>\n</table>\n\n<hr width=50>\n\nThen, validate the configuration like this:\n\n```python\nfrom jsonvv import JsonValidator, JvvRuntimeException\n\nval = JsonValidator(validator)\ntry:\n val.validate(config)\nexcept JvvRuntimeException as exc:\n print(\"Failed:\", exc)\nelse:\n print('Config is valid!')\n```\n\n---\n\n## Table of Contents\n\n- [Key Syntax](#key-syntax)\n - [Regex patterns](#regex-patterns)\n - [Lists of possible values](#lists-of-possible-values)\n - [Required fields (`*`)](#required-fields-)\n - [Grouping keys (`$`)](#grouping-keys-)\n- [Supported Types](#supported-types)\n - [String (`str`)](#string-str)\n - [Integer (`int`)](#integer-int)\n - [Double (`double`)](#double-double)\n - [Boolean (`bool`)](#boolean-bool)\n - [Array (`array`)](#array-array)\n - [Tuple (`tuple`)](#tuple-tuple)\n - [Nested Dictionaries](#nested-dictionaries)\n - [Nil (`nil`)](#nil-nil)\n - [Any (`any`)](#any-any)\n - [Required fields (`*`)](#required-fields-)\n - [Type References (`@`)](#type-references-)\n- [Advanced Features](#advanced-features)\n - [Subtracting Domains (`-`)](#subtracting-domains--)\n - [Union Types (`|`)](#union-types-)\n - [Conditional Ranges and Values](#conditional-ranges-and-values)\n- [Error Handling](#error-handling)\n\n---\n\n## Keys Syntax\n\nDictionary keys can be specified in several possible ways:\n\n- `\"key\": \"type\"`\n- `\"key1,key2,key3\": \"type\"`\n- `\"/key\\d+/\": \"type\"`\n- `\"*required_key\": \"type\"`\n\n### Regex patterns\n\nTo use regex in a key, wrap it in `/ ... /`.\n\n**Syntax:**\n\n```python\n\"/key\\d+/\": \"type\"\n```\n\n### Lists of possible values\n\nTo specify a list of keys, use a comma-separated string.\n\n**Syntax:**\n\n```python\n\"key1,key2,key3\": \"type\"\n\"/k[ey]{2}1/,key2\": \"type\"\n```\n\nTo escape a comma, use `!`.\n\n### Required fields (`*`)\n\nFields marked with `*` are required. The validation will fail without them.\n\n**Syntax:**\n\n```python\n\"*key1\": \"type\"\n\"*/key\\d+/\": \"type\"\n```\n\n### Grouping keys (`$`)\n\nFields 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.\n\n**Syntax:**\n\n```python\n\"isEnabled$group1\": \"bool\"\n\"value$group1\": \"int[>0]\"\n```\n\nThis will require both `value` is set if and only if `isEnabled` is set.\n\nMultiple `$` can be used to create more complex group dependencies.\n\n---\n\n## Supported Types\n\n### String (`str`)\n\nRepresents a string value. Optionally, you can specify a regex pattern that the string must match.\n\n**Syntax:**\n\n- Basic string: `\"str\"`\n- With regex pattern: `\"str[regex_pattern]\"`\n- The escape character for regex is `\\`, and for commas is `_`.\n\n**Arguments:**\n\n- `regex_pattern`: A regular expression that the string must match. If not specified, any string is accepted.\n\n**Examples:**\n\n1. Basic string:\n\n ```python\n \"username\": \"str\"\n ```\n\n Accepts any string value for the key `username`.\n\n2. String with regex pattern:\n\n ```python\n \"fullname\": \"str[/[A-Z][a-z]+ [A-Z][a-z]+/]\"\n ```\n\n Accepts a string that matches the pattern of a first and last name starting with uppercase letters.\n\n### Integer (`int`)\n\nRepresents an integer value. You can specify conditions like exact values, ranges, and inequalities.\n\n**Syntax:**\n\n- Basic integer: `\"int\"`\n- With conditions: `\"int[conditions]\"`\n\n**Arguments:**\n\n- `conditions`: A comma-separated list of conditions.\n\n**Condition Operators:**\n\n- `==`: Equal to a specific value.\n- `>=`: Greater than or equal to a value.\n- `<=`: Less than or equal to a value.\n- `>`: Greater than a value.\n- `<`: Less than a value.\n- `range`: A range between two values (inclusive).\n\n**Examples:**\n\n1. Basic integer:\n\n ```python\n \"age\": \"int\"\n ```\n\n Accepts any integer value for the key `age`.\n\n2. Integer with conditions:\n\n ```python\n \"userage\": \"int[>=0, <=120]\"\n ```\n\n Accepts integer values between 0 and 120 inclusive.\n\n3. Specific values and ranges\n\n ```python\n \"rating\": \"int[1-5]\"\n \"rating\": \"int[1,2,3,4-5]\"\n ```\n\n Accepts integer values 1, 2, 3, 4, or 5.\n\n4. Ranges with negative numbers:\n\n ```python\n \"rating\": \"int[-100 - -90]\"\n ```\n\n Accepts integer values from -100 to -90.\n\n### Double (`double`)\n\nRepresents a floating-point number. Supports the same conditions as integers.\n\n**Syntax:**\n\n- Basic double: `\"double\"`\n- With conditions: `\"double[conditions]\"`\n\n**Arguments:**\n\n- `conditions`: A comma-separated list of conditions.\n\n**Examples:**\n\n1. Basic double:\n\n ```python\n \"price\": \"double\"\n ```\n\n Accepts any floating-point number for the key `price`.\n\n2. Double with conditions:\n\n ```python\n \"percentage\": \"double[>=0.0,<=100.0]\"\n ```\n\n Accepts double values between 0.0 and 100.0 inclusive.\n\n### Boolean (`bool`)\n\nRepresents a boolean value (`True` or `False`).\n\n**Syntax:**\n\n```python\n\"isActive\": \"bool\"\n```\n\nAccepts a boolean value for the key `isActive`.\n\n### Array (`array`)\n\nRepresents a list of elements of a specified type. You can specify conditions on the length of the array.\n\n**Syntax:**\n\n- Basic array: `\"array[element_type]\"`\n- With length conditions: `\"array[element_type,length_conditions]\"`\n\n**Arguments:**\n\n- `element_type`: The type of the elements in the array.\n- `length_conditions`: Conditions on the array length (same as integer conditions).\n\n**Examples:**\n\n1. Basic array:\n\n ```python\n \"tags\": \"array[str]\"\n ```\n\n Accepts a list of strings for the key `tags`.\n\n2. Array with length conditions:\n\n ```python\n \"scores\": \"array[int[>=0,<=100],>=1,<=5]\"\n ```\n\n Accepts a list of 1 to 5 integers between 0 and 100 inclusive.\n\n3. Fixed-length array:\n\n ```python\n \"coordinates\": \"array[double, 2]\"\n ```\n\n Accepts a list of exactly 2 double values.\n\n4. More complex restraints:\n ```python\n \"coordinates\": \"array[array[int[>0]] - tuple[1, 1]], 2]\"\n ```\n\n### Tuple (`tuple`)\n\nRepresents a fixed-size sequence of elements of specified types.\n\n**Syntax:**\n\n```python\n\"tuple[element_type1, element_type2]\"\n```\n\n**Arguments:**\n\n- `element_typeN`: The type of the Nth element in the tuple.\n\n**Examples:**\n\n1. Basic tuple:\n\n ```python\n \"point\": \"tuple[int, int]\"\n ```\n\n Accepts a tuple or list of two integers.\n\n2. Tuple with mixed types:\n\n ```python\n \"userInfo\": \"tuple[str, int, bool]\"\n ```\n\n Accepts a tuple of a string, an integer, and a boolean.\n\n### Nested Dictionaries\n\nRepresents a nested dictionary structure. Dictionaries are defined using Python's dictionary syntax `{}` in the type definitions.\n\n**Syntax:**\n\n```python\n\"settings\": {\n \"volume\": \"int[>=0,<=100]\",\n \"brightness\": \"int[>=0,<=100]\",\n \"mode\": \"str\"\n}\n```\n\n**Usage:**\n\n- Define the expected keys and their types within the dictionary.\n- You can use all the supported types for the values.\n\n**Examples:**\n\n1. Nested dictionary:\n\n ```python\n \"user\": {\n \"name\": \"str\",\n \"age\": \"int[>=0]\",\n \"preferences\": {\n \"theme\": \"str\",\n \"notifications\": \"bool\"\n }\n }\n ```\n\n Defines a nested dictionary structure for the key `user`.\n\n### Nil (`nil`)\n\nRepresents a `None` value.\n\n**Syntax:**\n\n```python\n\"optionalValue\": \"int | nil\"\n```\n\n**Usage:**\n\n- Use `nil` to allow a value to be `None`.\n- Often used with union types to specify optional values.\n\n### Any (`any`)\n\nRepresents any value.\n\n**Syntax:**\n\n```python\n\"metadata\": \"any\"\n```\n\n**Usage:**\n\n- Use `any` when any value is acceptable.\n- Useful for keys where the value is not constrained.\n\n### Type References (`@`)\n\nAllows you to define reusable types and reference them.\n\n**Syntax:**\n\n- Define a named type:\n\n ```python\n \"@typeName\": \"type_definition\"\n ```\n\n- Reference a named type:\n\n ```python\n \"key\": \"@typeName\"\n ```\n\n**Examples:**\n\n1. Defining and using a named type:\n\n ```python\n \"@positiveInt\": \"int[>0]\"\n \"userId\": \"@positiveInt\"\n ```\n\n Defines a reusable type `@positiveInt` and uses it for the key `userId`.\n\n---\n\n## Advanced Features\n\n### Subtracting Domains (`-`)\n\nAllows you to specify that a value should not match a certain type or condition.\n\n**Syntax:**\n\n```python\n\"typeA - typeB\"\n```\n\n**Usage:**\n\n- The value must match `typeA` but not `typeB`.\n\n**Examples:**\n\n1. Excluding certain strings:\n\n ```python\n \"message\": \"str - str[.*error.*]\"\n ```\n\n Accepts any string that does not match the regex pattern `.*error.*`.\n\n2. Excluding a range of numbers:\n\n ```python\n \"score\": \"int[0-100] - int[>=90]\"\n ```\n\n Accepts integers between 0 and 100, excluding values greater than or equal to 90.\n\n3. Excluding multiple types:\n\n ```python\n \"score\": \"int[>0,<100] - int[>90] - int[<10]\"\n # Union, then subtraction:\n \"score\": \"int[>0,<100] - int[>90] | int[<10]\"\n \"score\": \"int[>0,<100] - (int[>90] | int[<10])\" # same thing\n # Use parenthesis to run subtraction first\n \"score\": \"int[>0,<50] | (int[<100] - int[<10])\"\n \"score\": \"(int[<100] - int[<10]) | int[>0,<50]\"\n ```\n\n **Note**: Union is handled before subtraction.\n\n4. Allowing all but a specific value:\n\n ```python\n \"specialNumber\": \"any - int[0]\"\n ```\n\n### Union Types (`|`)\n\nAllows you to specify that a value can be one of multiple types.\n\n**Syntax:**\n\n```python\n\"typeA | typeB | typeC\"\n```\n\n**Usage:**\n\n- The value must match at least one of the specified types.\n\n**Examples:**\n\n1. Multiple possible types:\n\n ```python\n \"data\": \"int | str | bool\"\n ```\n\n Accepts an integer, string, or boolean value for the key `data`.\n\n2. Combining with arrays:\n\n ```python\n \"mixedList\": \"array[int | str]\"\n ```\n\n Accepts a list of integers or strings.\n\n### Conditional Ranges and Values\n\nSpecifies conditions that values must satisfy, including ranges and specific values.\n\n**Syntax:**\n\n- Greater than: `\">value\"`\n- Less than: `\"<value\"`\n- Greater than or equal to: `\">=value\"`\n- Less than or equal to: `\"<=\"value\"`\n- Range: `\"start-end\"`\n- Specific values: `\"value1,value2,value3\"`\n\n**Examples:**\n\n1. Integer conditions:\n\n ```python\n \"level\": \"int[>=1,<=10]\"\n ```\n\n Accepts integers from 1 to 10 inclusive.\n\n2. Double with range:\n\n ```python\n \"latitude\": \"double[-90.0 - 90.0]\"\n ```\n\n Accepts doubles between -90.0 and 90.0 inclusive.\n\n3. Specific values:\n\n ```python\n \"status\": \"int[1,2,3]\"\n ```\n\n Accepts integers that are either 1, 2, or 3.\n\n---\n\n## Error Handling\n\n```mermaid\ngraph TD\n Exception --> JvvException\n JvvException --> JvvRuntimeException\n JvvException --> JvvSyntaxError\n\n JvvRuntimeException --> UnknownProperty[\"UnknownProperty<br/><small>Raised when a key in config<br/>isn't defined in property types</small>\"]\n JvvRuntimeException --> InvalidPropertyType[\"InvalidPropertyType<br/><small>Raised when a value doesn't<br/>match its type definition</small>\"]\n InvalidPropertyType --> MissingRequiredKey[\"MissingRequiredKey<br/><small>Raised when a required key<br/>is missing from config</small>\"]\n MissingRequiredKey --> MissingGroupKey[\"MissingGroupKey<br/><small>Raised when some keys in a<br/>property group are missing</small>\"]\n\n JvvSyntaxError --> PropertySyntaxError[\"PropertySyntaxError<br/><small>Raised when property type<br/>definitions have syntax errors</small>\"]\n\n classDef base fill:#eee,stroke:#333,stroke-width:2px;\n classDef jvv fill:#d4e6f1,stroke:#2874a6,stroke-width:2px;\n classDef runtime fill:#d5f5e3,stroke:#196f3d,stroke-width:2px;\n classDef syntax fill:#fdebd0,stroke:#b9770e,stroke-width:2px;\n classDef error fill:#fadbd8,stroke:#943126,stroke-width:2px;\n\n class Exception base;\n class JvvException jvv;\n class JvvRuntimeException,JvvSyntaxError runtime;\n class PropertySyntaxError syntax;\n class UnknownProperty,InvalidPropertyType,MissingRequiredKey,MissingGroupKey error;\n```\n\n---\n\n### Types\n\n- **str**: Basic string type.\n\n - Arguments:\n - `regex_pattern` (optional): A regex pattern the string must match.\n - Example: `\"str[^[A-Za-z]+$]\"`\n\n- **int**: Integer type with conditions.\n\n - Arguments:\n - `conditions`: Inequalities (`>=`, `<=`, `>`, `<`), specific values (`value1,value2`), ranges (`start-end`).\n - Example: `\"int[>=0,<=100]\"`\n\n- **double**: Double (floating-point) type with conditions.\n\n - Arguments:\n - Same as `int`.\n - Example: `\"double[>0.0]\"`\n\n- **bool**: Boolean type.\n\n - Arguments: None.\n - Example: `\"bool\"`\n\n- **array**: Array (list) of elements of a specified type.\n\n - Arguments:\n - `element_type`: Type of elements in the array.\n - `length_conditions` (optional): Conditions on the array length.\n - Example: `\"array[int[>=0],>=1,<=10]\"`\n\n- **tuple**: Fixed-size sequence of elements of specified types.\n\n - Arguments:\n - List of element types.\n - Example: `\"tuple[str, int, bool]\"`\n\n- **nil**: Represents a `None` value.\n\n - Arguments: None.\n - Example: `\"nil\"`\n\n- **any**: Accepts any value.\n\n - Arguments: None.\n - Example: `\"any\"`\n\n- **Type References**: Reusable type definitions.\n - Arguments:\n - `@typeName`: Reference to a named type.\n - Example:\n - Define: `\"@positiveInt\": \"int[>0]\"`\n - Use: `\"userId\": \"@positiveInt\"`\n\n### Type Combinations\n\n- **Union Types** (`|`): Value must match one of multiple types.\n\n - Syntax: `\"typeA | typeB\"`\n - Example: `\"str | int\"`\n\n- **Subtracting Domains** (`-`): Value must match `typeA` but not `typeB`.\n - Syntax: `\"typeA - typeB\"`\n - Example: `\"int - int[13]\"` (any integer except 13)\n\n### Escaping Characters\n\n- `!`: Escapes commas, slashes, and other jsonvv characters within strings.\n- `\\`: Escapes within a regex pattern.\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "JSON value validator",
"version": "0.2.2",
"project_urls": {
"Homepage": "https://github.com/daijro/camoufox/tree/main/pythonlib/jsonvv",
"Repository": "https://github.com/daijro/camoufox"
},
"split_keywords": [
"json",
" validator",
" validation",
" typing"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "b314e69b23dc5e7b69aa5b2a4e0f5d60ca280865bebb9e7fd4db4974574e5589",
"md5": "3722b5b8cefd5979302d48daf352ce4b",
"sha256": "bb574c7c3cea87106516e98f67dbcd7fde59a0e5d3ad0cef2ddb6cc3f9c9102f"
},
"downloads": -1,
"filename": "jsonvv-0.2.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "3722b5b8cefd5979302d48daf352ce4b",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.8",
"size": 15186,
"upload_time": "2024-11-28T04:57:49",
"upload_time_iso_8601": "2024-11-28T04:57:49.745807Z",
"url": "https://files.pythonhosted.org/packages/b3/14/e69b23dc5e7b69aa5b2a4e0f5d60ca280865bebb9e7fd4db4974574e5589/jsonvv-0.2.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "0a01d7a719321baf7a93d444054840d9ca17c4bd5ccde58cb7af7b7e620762c2",
"md5": "842cff54c783a034dfb81ea521d15fd8",
"sha256": "0eec3a6515ce05387d73d412a7505318136267a511cbdd4dbeb63959a1a751c6"
},
"downloads": -1,
"filename": "jsonvv-0.2.2.tar.gz",
"has_sig": false,
"md5_digest": "842cff54c783a034dfb81ea521d15fd8",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.8",
"size": 17313,
"upload_time": "2024-11-28T04:57:51",
"upload_time_iso_8601": "2024-11-28T04:57:51.296906Z",
"url": "https://files.pythonhosted.org/packages/0a/01/d7a719321baf7a93d444054840d9ca17c4bd5ccde58cb7af7b7e620762c2/jsonvv-0.2.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-11-28 04:57:51",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "daijro",
"github_project": "camoufox",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "jsonvv"
}