# JSON Schema to Pydantic

A Python library for automatically generating Pydantic v2 models from JSON Schema definitions.

![PyPI - Version](https://img.shields.io/pypi/v/json-schema-to-pydantic)
![PyPI - Downloads](https://img.shields.io/pypi/dm/json-schema-to-pydantic?logo=pypi)
[![codecov](https://codecov.io/github/richard-gyiko/json-schema-to-pydantic/graph/badge.svg?token=YA2Y769H1K)](https://codecov.io/github/richard-gyiko/json-schema-to-pydantic)

## Features

- Converts JSON Schema to Pydantic v2 models
- Supports complex schema features including:
  - References ($ref) with circular reference detection
  - Combiners (allOf, anyOf, oneOf) with proper type discrimination
  - Type constraints and validations
  - Array and object validations
  - Format validations (email, uri, uuid, date-time)
  - Top-level arrays and scalar types (not just objects)
  - Underscore-prefixed fields (common in OpenAPI specs)
- Full type hinting support
- Clean, simple API

## Installation

```bash
pip install json-schema-to-pydantic
```

## Development Setup

1. Clone the repository
2. Install development dependencies:
```bash
# Using uv (recommended)
uv pip install -e ".[dev]"

# Or using pip
pip install -e ".[dev]"
```

3. Run tests:
```bash
pytest
```

## Quick Start

```python
from json_schema_to_pydantic import create_model

# Define your JSON Schema
schema = {
    "title": "User",
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "email": {"type": "string", "format": "email"},
        "age": {"type": "integer", "minimum": 0}
    },
    "required": ["name", "email"]
}

# Generate your Pydantic model
UserModel = create_model(schema)

# Use the model
user = UserModel(
    name="John Doe",
    email="john@example.com",
    age=30
)

# Example with relaxed validation
RelaxedModel = create_model(
    {
        "type": "object",
        "properties": {
            "tags": {"type": "array"},  # Array without items schema
            "metadata": {}  # Field without type
        }
    },
    allow_undefined_array_items=True,  # Allows arrays without items schema
    allow_undefined_type=True  # Allows fields without type (defaults to Any)
)
relaxed_instance = RelaxedModel(
    tags=[1, "two", True],
    metadata={"custom": "data"}
)

# Example with underscore-prefixed fields (common in OpenAPI)
OpenAPIModel = create_model(
    {
        "type": "object",
        "properties": {
            "_links": {"type": "object"},
            "_embedded": {"type": "object"}
        }
    },
    populate_by_name=True  # Allows access via both '_links' and 'links'
)
```

## Advanced Usage

For more complex scenarios, you can use the `PydanticModelBuilder` directly:

```python
from pydantic import BaseModel
from typing_extensions import TypeAliasType
from json_schema_to_pydantic import PydanticModelBuilder

class PetModel(BaseModel):
    name: str
    type: str

SomeType = TypeAliasType("SomeType", list[str])

builder = PydanticModelBuilder(
    predefined_models={"#/definitions/Pet": PetModel},
    predefined_refs={"#/definitions/SomeType": SomeType},
)
model = builder.create_pydantic_model(schema, root_schema)
```

You can also pass `predefined_models` to `create_model(...)` directly.
When a `$ref` key matches an entry in `predefined_models`, that class is reused
instead of generating a new class.
For non-model aliases (such as `list[str]` or `TypeAliasType`), use
`predefined_refs`.

## Error Handling

The library provides specific exceptions for different error cases:

```python
from json_schema_to_pydantic import (
    SchemaError,     # Base class for all schema errors
    TypeError,       # Invalid or unsupported type
    CombinerError,   # Error in schema combiners
    ReferenceError,  # Error in schema references
)

try:
    model = create_model(schema)
except TypeError as e:
    print(f"Invalid type in schema: {e}")
except ReferenceError as e:
    print(f"Invalid reference: {e}")
```

## Documentation

See [docs/features.md](docs/features.md) for detailed documentation of supported JSON Schema features.

## Contributing

1. Fork the repository
2. Create a new branch for your feature
3. Make your changes
4. Run tests and ensure they pass
5. Submit a pull request

## License

This project is licensed under the terms of the license included in the repository.
