Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,79 @@ cross-field validator will not run:
schema({'password': '123', 'password_again': 1337})
```

## JSON Schema Export

Voluptuous schemas can be exported to [JSON Schema](https://json-schema.org/) format, enabling integration with modern IDEs, API documentation tools, and other validation systems.

### Basic Usage

Use the `to_json_schema()` method on any Schema instance:

```pycon
>>> from voluptuous import Schema, Required, Optional, Range, Email
>>> schema = Schema({
... Required('name'): str,
... Required('email'): Email(),
... Optional('age'): Range(min=0, max=150)
... })
>>> json_schema = schema.to_json_schema()
>>> json_schema['type']
'object'
>>> json_schema['required']
['name', 'email']
>>> json_schema['properties']['email']['format']
'email'
```

Alternatively, use the standalone `to_json_schema()` function:

```pycon
>>> from voluptuous import to_json_schema
>>> json_schema = to_json_schema(Schema(str))
>>> json_schema['type']
'string'
```

### Supported Validators

Most voluptuous validators are converted to equivalent JSON Schema constraints:

- **Range**: Converted to `minimum`/`maximum` constraints
- **Length**: Converted to `minLength`/`maxLength` for strings, `minItems`/`maxItems` for arrays
- **Email**: Converted to `format: "email"`
- **Url**: Converted to `format: "uri"`
- **Date**: Converted to `format: "date"`
- **Datetime**: Converted to `format: "date-time"`
- **Match**: Converted to `pattern` constraint
- **In**: Converted to `enum` constraint
- **All**: Converted to `allOf` constraint
- **Any**: Converted to `anyOf` constraint

### Use Cases

The exported JSON Schema can be used with:

- **IDEs**: For YAML/JSON file validation and autocompletion
- **API Documentation**: Tools like OpenAPI/Swagger
- **Code Generation**: Generate types for other languages
- **Validation Libraries**: Use with JSON Schema validators in any language

### Example

```pycon
>>> from voluptuous import Schema, Required, All, Length, Range, In
>>> api_schema = Schema({
... Required('endpoint'): All(str, Length(min=1)),
... Required('method'): In(['GET', 'POST', 'PUT', 'DELETE']),
... Optional('timeout'): Range(min=1, max=300),
... Optional('retries', default=3): Range(min=0, max=10)
... })
>>> json_schema = api_schema.to_json_schema()
>>> # Use json_schema with your favorite JSON Schema validator
```

For more examples, see `examples/json_schema_export.py`.

## Running tests

Voluptuous is using `pytest`:
Expand Down
261 changes: 261 additions & 0 deletions examples/json_schema_export.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
#!/usr/bin/env python3
"""
JSON Schema Export Examples for Voluptuous

This script demonstrates how to use the new JSON Schema export functionality
in voluptuous to convert voluptuous schemas to JSON Schema format.

The JSON Schema output can be used with:
- IDEs for YAML/JSON validation
- API documentation tools
- Schema validation libraries
- Code generation tools
"""

import json

from voluptuous import (
All,
Any,
Clamp,
Coerce,
Date,
Datetime,
Email,
ExactSequence,
In,
Length,
Match,
Optional,
Range,
Required,
Schema,
Url,
to_json_schema,
)


def example_basic_types():
"""Demonstrate basic type conversion."""
print("=== Basic Types ===")

# Simple types
schemas = {
"String": Schema(str),
"Integer": Schema(int),
"Float": Schema(float),
"Boolean": Schema(bool),
"Literal": Schema("hello"),
"None": Schema(None),
}

for name, schema in schemas.items():
json_schema = schema.to_json_schema()
print(f"{name}: {json.dumps(json_schema, indent=2)}")
print()


def example_object_schemas():
"""Demonstrate object schema conversion."""
print("=== Object Schemas ===")

# User profile schema
user_schema = Schema(
{
Required('username'): All(str, Length(min=3, max=20)),
Required('email'): Email(),
Optional('age'): Range(min=13, max=120),
Optional('bio', default=""): All(str, Length(max=500)),
Optional('preferences'): {
'theme': In(['light', 'dark', 'auto']),
'notifications': bool,
'language': str,
},
}
)

json_schema = user_schema.to_json_schema()
print("User Profile Schema:")
print(json.dumps(json_schema, indent=2))
print()


def example_array_schemas():
"""Demonstrate array schema conversion."""
print("=== Array Schemas ===")

# Simple array
simple_array = Schema([str])
print("Simple String Array:")
print(json.dumps(simple_array.to_json_schema(), indent=2))
print()

# Mixed array with exact sequence
exact_sequence = Schema(ExactSequence([str, int, bool]))
print("Exact Sequence [str, int, bool]:")
print(json.dumps(exact_sequence.to_json_schema(), indent=2))
print()

# Set (unique items)
unique_strings = Schema({str})
print("Set of Strings (unique items):")
print(json.dumps(unique_strings.to_json_schema(), indent=2))
print()


def example_validators():
"""Demonstrate validator conversion."""
print("=== Validators ===")

validators = {
"Range": Schema(Range(min=1, max=100)),
"Length": Schema(All(str, Length(min=2, max=50))),
"Email": Schema(Email()),
"URL": Schema(Url()),
"Date": Schema(Date()),
"DateTime": Schema(Datetime()),
"Pattern": Schema(Match(r'^[A-Z][a-z]+$')),
"Enum": Schema(In(['red', 'green', 'blue'])),
"Coerce": Schema(Coerce(int)),
}

for name, schema in validators.items():
json_schema = schema.to_json_schema()
print(f"{name}:")
print(json.dumps(json_schema, indent=2))
print()


def example_composite_validators():
"""Demonstrate composite validator conversion."""
print("=== Composite Validators ===")

# All validator (must pass all conditions)
all_validator = Schema(All(str, Length(min=1), Match(r'^[a-zA-Z]+$')))
print("All(str, Length(min=1), Match('^[a-zA-Z]+$')):")
print(json.dumps(all_validator.to_json_schema(), indent=2))
print()

# Any validator (must pass at least one condition)
any_validator = Schema(Any(str, int, bool))
print("Any(str, int, bool):")
print(json.dumps(any_validator.to_json_schema(), indent=2))
print()


def example_complex_schema():
"""Demonstrate a complex, real-world schema."""
print("=== Complex Real-World Schema ===")

# API configuration schema
api_config_schema = Schema(
{
Required('api'): {
Required('name'): All(str, Length(min=1, max=100)),
Required('version'): Match(r'^\d+\.\d+\.\d+$'),
Required('endpoints'): [
{
Required('path'): All(str, Match(r'^/[a-zA-Z0-9/_-]*$')),
Required('method'): In(
['GET', 'POST', 'PUT', 'DELETE', 'PATCH']
),
Optional('auth_required', default=True): bool,
Optional('rate_limit'): Range(min=1, max=10000),
Optional('description'): All(str, Length(max=500)),
}
],
Optional('database'): {
Required('host'): str,
Required('port'): Range(min=1, max=65535),
Required('name'): All(str, Length(min=1, max=64)),
Optional('ssl', default=True): bool,
Optional('timeout', default=30): Range(min=1, max=300),
},
},
Optional('logging'): {
'level': In(['DEBUG', 'INFO', 'WARNING', 'ERROR']),
'format': str,
'file': str,
},
Optional('features'): {str: bool}, # Feature flags
}
)

json_schema = api_config_schema.to_json_schema()
print("API Configuration Schema:")
print(json.dumps(json_schema, indent=2))
print()


def example_usage_with_data():
"""Show how the exported schema can validate actual data."""
print("=== Usage Example ===")

# Define a schema
person_schema = Schema(
{
Required('name'): All(str, Length(min=1, max=100)),
Required('email'): Email(),
Optional('age'): Range(min=0, max=150),
Optional('tags'): [str],
}
)

# Export to JSON Schema
json_schema = person_schema.to_json_schema()

# Sample data that would be valid
sample_data = {
"name": "John Doe",
"email": "john@example.com",
"age": 30,
"tags": ["developer", "python"],
}

print("Voluptuous Schema:")
print(f"Schema: {person_schema}")
print()

print("Exported JSON Schema:")
print(json.dumps(json_schema, indent=2))
print()

print("Sample Valid Data:")
print(json.dumps(sample_data, indent=2))
print()

# Validate with voluptuous
try:
validated = person_schema(sample_data)
print("✓ Data is valid according to voluptuous schema")
print(f"Validated data: {validated}")
except Exception as e:
print(f"✗ Validation failed: {e}")


def main():
"""Run all examples."""
print("Voluptuous JSON Schema Export Examples")
print("=" * 50)
print()

example_basic_types()
example_object_schemas()
example_array_schemas()
example_validators()
example_composite_validators()
example_complex_schema()
example_usage_with_data()

print("=" * 50)
print("Examples completed!")
print()
print("You can now use these JSON Schemas with:")
print("- JSON Schema validators")
print("- IDE validation for YAML/JSON files")
print("- API documentation tools")
print("- Code generation tools")


if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ python_version = "3.9"
warn_unused_ignores = true

[tool.pytest.ini_options]
python_files = "tests.py"
python_files = "tests.py test_*.py"
testpaths = "voluptuous/tests"
addopts = "--doctest-glob=*.md -v"
2 changes: 2 additions & 0 deletions voluptuous/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@
True
"""

from voluptuous.json_schema import to_json_schema

# flake8: noqa
# fmt: off
from voluptuous.schema_builder import *
Expand Down
Loading