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 }); } };