JSON Schema for Polyglot Engineers
Table of Contents
- 1. JSON Schema Through the Lens of Type Systems
- 2. Practical Examples
- 3. Advanced Pattern Matching
- 4. Validation Functions
- 5. Recursive Types
- 6. Testing and Validation
- 7. Practical Applications
- 8. Code Generation Examples
- 9. Common Patterns and Best Practices
- 10. Babel Code Blocks for Testing
- 11. Integration Examples
- 12. Resources and Further Reading
1. JSON Schema Through the Lens of Type Systems
1.1. Overview
JSON Schema can be understood through familiar type system concepts from various languages:
Language | Concept | JSON Schema Equivalent |
---|---|---|
Haskell | ADTs | oneOf, anyOf |
TypeScript | Union Types | oneOf |
Clojure | Spec | properties, patterns |
Python | Type Hints | type definitions |
Scheme | Contracts | validation keywords |
1.2. Type System Parallels
// TypeScript union type type Status = "active" | "inactive" | "pending"
{ "type": "string", "enum": ["active", "inactive", "pending"] }
2. Practical Examples
2.1. Record Types (Haskell → JSON Schema)
data User = User { userId :: Int , userName :: String , userEmail :: Maybe String }
{ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "userId": { "type": "integer" }, "userName": { "type": "string" }, "userEmail": { "oneOf": [ { "type": "string", "format": "email" }, { "type": "null" } ] } }, "required": ["userId", "userName"] }
2.2. Clojure Spec Translation
(require '[clojure.spec.alpha :as s]) (s/def ::age (s/and int? #(>= % 0))) (s/def ::name string?) (s/def ::person (s/keys :req-un [::name ::age]))
{ "type": "object", "properties": { "age": { "type": "integer", "minimum": 0 }, "name": { "type": "string" } }, "required": ["name", "age"] }
2.3. TypeScript Interface Mapping
interface ApiResponse<T> { status: 'success' | 'error'; data?: T; error?: { code: number; message: string; }; }
{ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "status": { "type": "string", "enum": ["success", "error"] }, "data": { "$comment": "Generic type parameter T", "description": "Any valid JSON value" }, "error": { "type": "object", "properties": { "code": { "type": "integer" }, "message": { "type": "string" } }, "required": ["code", "message"] } }, "required": ["status"], "allOf": [ { "if": { "properties": { "status": { "const": "success" } } }, "then": { "required": ["data"] } }, { "if": { "properties": { "status": { "const": "error" } } }, "then": { "required": ["error"] } } ] }
3. Advanced Pattern Matching
3.1. Haskell-style Pattern Matching
data Shape = Circle Double | Rectangle Double Double | Triangle Double Double Double
{ "type": "object", "oneOf": [ { "type": "object", "properties": { "type": { "const": "circle" }, "radius": { "type": "number" } }, "required": ["type", "radius"] }, { "type": "object", "properties": { "type": { "const": "rectangle" }, "width": { "type": "number" }, "height": { "type": "number" } }, "required": ["type", "width", "height"] }, { "type": "object", "properties": { "type": { "const": "triangle" }, "a": { "type": "number" }, "b": { "type": "number" }, "c": { "type": "number" } }, "required": ["type", "a", "b", "c"] } ] }
4. Validation Functions
4.1. Python Type Validation
from typing import TypedDict, Literal, Union from dataclasses import dataclass class Success(TypedDict): status: Literal['success'] data: dict class Error(TypedDict): status: Literal['error'] message: str Response = Union[Success, Error]
{ "$schema": "http://json-schema.org/draft-07/schema#", "oneOf": [ { "type": "object", "properties": { "status": { "const": "success" }, "data": { "type": "object" } }, "required": ["status", "data"] }, { "type": "object", "properties": { "status": { "const": "error" }, "message": { "type": "string" } }, "required": ["status", "message"] } ] }
5. Recursive Types
5.1. Scheme-like List Structure
(define-record-type <tree-node> (make-node value left right) node? (value node-value) (left node-left) (right node-right))
ice-9/boot-9.scm:1676:22: In procedure raise-exception: Unbound variable: define-record-type Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Binary Tree Node", "$defs": { "node": { "type": "object", "properties": { "value": { "type": "number" }, "left": { "oneOf": [ { "$ref": "#/$defs/node" }, { "type": "null" } ] }, "right": { "oneOf": [ { "$ref": "#/$defs/node" }, { "type": "null" } ] } }, "required": ["value"] } }, "$ref": "#/$defs/node" }
6. Testing and Validation
6.1. Property-Based Testing (QuickCheck Style)
import Test.QuickCheck prop_validJson :: User -> Property prop_validJson user = validateSchema userSchema (toJSON user) === Right True
from hypothesis import given from hypothesis.strategies import builds, text, integers @given(builds(User, user_id=integers(min_value=0), user_name=text())) def test_user_schema(user): assert validate_schema(user_schema, user.dict())
7. Practical Applications
7.1. API Contract Definition
// TypeScript API definition type APIEndpoint = { path: string; method: 'GET' | 'POST' | 'PUT' | 'DELETE'; requestBody?: unknown; response: unknown; }
{ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "path": { "type": "string", "pattern": "^/" }, "method": { "type": "string", "enum": ["GET", "POST", "PUT", "DELETE"] }, "requestBody": { "$comment": "Schema for request body" }, "response": { "$comment": "Schema for response body" } }, "required": ["path", "method", "response"] }
8. Code Generation Examples
8.1. Generate TypeScript from Schema
// Generated from JSON Schema interface User { userId: number; userName: string; userEmail?: string | null; } // Usage with type safety const user: User = { userId: 1, userName: "test", userEmail: null };
8.2. Generate Python Dataclasses
# Generated from JSON Schema @dataclass class User: user_id: int user_name: str user_email: Optional[str] = None # Usage with type hints user = User(user_id=1, user_name="test")
9. Common Patterns and Best Practices
9.1. Algebraic Data Types
data Either a b = Left a | Right b
{ "oneOf": [ { "type": "object", "properties": { "type": { "const": "Left" }, "value": { "$ref": "#/$defs/a" } }, "required": ["type", "value"] }, { "type": "object", "properties": { "type": { "const": "Right" }, "value": { "$ref": "#/$defs/b" } }, "required": ["type", "value"] } ] }
10. Babel Code Blocks for Testing
import json from jsonschema import validate schema = { "type": "object", "properties": { "name": {"type": "string"}, "age": {"type": "integer", "minimum": 0} }, "required": ["name"] } # Valid validate({"name": "John", "age": 30}, schema) print("Valid schema validated successfully") # This would raise ValidationError # validate({"age": "invalid"}, schema)
11. Integration Examples
11.1. Express.js Middleware
import { validate } from 'jsonschema'; const validateMiddleware = (schema: any) => ( req: Request, res: Response, next: NextFunction ) => { const result = validate(req.body, schema); if (result.valid) { next(); } else { res.status(400).json({ errors: result.errors }); } };