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 to 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)
Valid schema validated successfully
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 });
}
};