How to Validate JSON Output Against a Dict Schema in Python

Validate JSON-like data against a simple dict schema with type checking and descriptive error messages using only the Python standard library.

Easy Python 3.9+ Aug 9, 2026 AI & LLM integration patterns 13 views 0 copies

Python code

82 lines
Python 3.9+
from typing import Dict, Any, List, Union

def validate_json(data: Any, schema: Dict[str, str]) -> List[str]:
    """
    Validate JSON-like data against a simple dict schema.
    Schema format: {field_name: expected_type} where type is one of:
    'str', 'int', 'float', 'bool', 'list', 'dict', 'any'
    Returns list of validation errors.
    """
    errors = []
    
    if not isinstance(data, dict):
        return ["Root must be a dict"]
    
    for field, expected_type in schema.items():
        if field not in data:
            errors.append(f"Missing field: {field}")
            continue
        
        value = data[field]
        type_map = {
            'str': str,
            'int': int,
            'float': (int, float),
            'bool': bool,
            'list': list,
            'dict': dict,
        }
        
        if expected_type != 'any':
            expected = type_map[expected_type]
            if not isinstance(value, expected):
                errors.append(
                    f"Field '{field}' should be {expected_type}, got {type(value).__name__}"
                )
    
    return errors


def main():
    schema = {
        "name": "str",
        "age": "int",
        "score": "float",
        "active": "bool",
        "tags": "list",
        "metadata": "dict"
    }
    
    # Valid example
    valid_data = {
        "name": "Alice",
        "age": 30,
        "score": 87.5,
        "active": True,
        "tags": ["python", "json"],
        "metadata": {"level": "advanced"}
    }
    
    # Invalid example with wrong types and missing field
    invalid_data = {
        "name": "Bob",
        "age": "thirty",  # should be int
        "score": 92,        # int instead of float - allowed via int,float
        "active": "yes",    # should be bool
        # "tags" missing
        "metadata": {"level": "beginner"}
    }
    
    print("=== Valid Data ===")
    valid_errors = validate_json(valid_data, schema)
    print("Errors:", valid_errors if valid_errors else "None - validation passed")
    
    print("\n=== Invalid Data ===")
    invalid_errors = validate_json(invalid_data, schema)
    for error in invalid_errors:
        print(f"  - {error}")
    print(f"Total errors found: {len(invalid_errors)}")


if __name__ == "__main__":
    main()

Output

stdout
=== Valid Data ===
Errors: None - validation passed

=== Invalid Data ===
  - Field 'age' should be int, got str
  - Field 'active' should be bool, got str
  - Missing field: tags
Total errors found: 3

How it works

The validate_json function iterates through each field in the schema dict and checks presence and type against the provided data. The type_map dictionary maps schema type names to Python built-in types for isinstance checks. Using (int, float) for the float type allows both integers and floats, which is common in JSON payloads. The function returns a list of error strings, making it easy to surface validation issues in API responses or LLM output checks. This pattern is a lightweight alternative to pydantic when you only need basic type validation without full object modeling.

Common mistakes

  • Using `json.loads` on data that's already a dict — validate native dicts directly
  • Forgetting that bool is a subclass of int in Python, so ordering matters in isinstance checks
  • Not validating nested structures recursively when fields are dicts or lists
  • Assuming float validation should reject integers instead of allowing them via (int, float)

Variations

  1. Use `pydantic` with a BaseModel for automatic validation and coercion of nested structures
  2. Recursively validate nested dict/list fields by calling validate_json on nested values

Real-world use cases

  • Validating structured output from LLM JSON responses before inserting into a database.
  • Schema-checking API webhook payloads at runtime to surface bad requests early.
  • Verifying config files or environment variables match expected types during service startup.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from AI & LLM integration patterns

Related tutorials and quizzes for this topic.