JSON Schema: The Complete Guide

General ~3 min read
JSONSchemaValidationAPI DesignOpenAPI

Master JSON Schema from basics to advanced features. Learn validation keywords, composition with allOf/anyOf/oneOf, conditional schemas, and real-world examples for API design.

What is JSON Schema?

JSON Schema is a declarative language for validating the structure of JSON data. It’s like a contract that defines what your JSON should look like.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "User",
  "description": "A user in the system",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "age": {
      "type": "integer",
      "minimum": 0,
      "maximum": 150
    },
    "email": {
      "type": "string",
      "format": "email"
    }
  },
  "required": ["name", "age"],
  "additionalProperties": false
}

Basic Types

Strings

{
  "name": { "type": "string", "pattern": "^[A-Za-z]+$" },
  "email": { "type": "string", "format": "email" }
}

Formats: email, uri, date, date-time, time, ipv4, ipv6, uuid

Numbers

{
  "type": "number",
  "minimum": 0,
  "exclusiveMaximum": 100,
  "multipleOf": 0.01
}

Arrays

{
  "type": "array",
  "items": { "type": "string" },
  "minItems": 1,
  "maxItems": 10,
  "uniqueItems": true
}

Objects

{
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "age": { "type": "integer" }
  },
  "required": ["name"],
  "additionalProperties": false,
  "minProperties": 1,
  "maxProperties": 10
}

Composition

allOf (AND)

{
  "allOf": [
    { "type": "object", "properties": { "name": { "type": "string" } } },
    { "type": "object", "properties": { "age": { "type": "integer" } } }
  ]
}

anyOf (OR)

{
  "anyOf": [
    { "type": "string" },
    { "type": "integer" }
  ]
}

oneOf (XOR)

{
  "oneOf": [
    { "required": ["email"] },
    { "required": ["phone"] }
  ]
}

$ref (Reuse)

{
  "$defs": {
    "address": {
      "type": "object",
      "properties": {
        "street": { "type": "string" },
        "city": { "type": "string" }
      }
    }
  },
  "type": "object",
  "properties": {
    "home": { "$ref": "#/$defs/address" },
    "work": { "$ref": "#/$defs/address" }
  }
}

Conditional Validation

{
  "type": "object",
  "properties": {
    "country": { "type": "string" },
    "postalCode": { "type": "string" }
  },
  "if": {
    "properties": { "country": { "const": "US" } },
    "required": ["country"]
  },
  "then": {
    "properties": {
      "postalCode": { "pattern": "^[0-9]{5}(-[0-9]{4})?$" }
    }
  },
  "else": {
    "properties": {
      "postalCode": { "minLength": 3 }
    }
  }
}

Real-World Examples

API Request Validation

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "username": {
      "type": "string",
      "minLength": 3,
      "maxLength": 20,
      "pattern": "^[a-zA-Z0-9_]+$"
    },
    "password": {
      "type": "string",
      "minLength": 8,
      "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).*$"
    },
    "email": {
      "type": "string",
      "format": "email"
    },
    "roles": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": ["admin", "user", "moderator"]
      },
      "uniqueItems": true
    }
  },
  "required": ["username", "password", "email"],
  "additionalProperties": false
}

Configuration File

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "database": {
      "type": "object",
      "properties": {
        "host": { "type": "string" },
        "port": { "type": "integer", "default": 5432 },
        "name": { "type": "string" }
      },
      "required": ["host", "name"]
    },
    "logging": {
      "type": "object",
      "properties": {
        "level": {
          "type": "string",
          "enum": ["debug", "info", "warn", "error"]
        },
        "file": { "type": "string" }
      }
    }
  },
  "required": ["database"]
}

Tooling

Validation Libraries

LanguageLibrary
JavaScriptajv, joi, zod
Pythonjsonschema, pydantic
Javaeverit-json-schema
Gogojsonschema
C#JsonSchema.Net

Code Generation

# TypeScript from JSON Schema
npx json-schema-to-typescript schema.json > types.ts

# Python from JSON Schema
datamodel-codegen --input schema.json --output models.py

Documentation

# Generate HTML documentation (JavaScript)
npx @adobe/jsonschema2md -d schema.json -o docs

# Generate HTML documentation (Python)
pip install json-schema-for-humans && generate-schema-doc schema.json docs.html

Best Practices

  1. Start simple — Add constraints as needed
  2. Use $ref — Don’t repeat yourself
  3. Version your schemas — Include $schema and version in $id
  4. Test thoroughly — Use both valid and invalid test cases
  5. Document formats — Custom formats need custom validators
  6. Consider performance — Complex schemas can be slow

JSON API Comparison Across Languages

Language Library Serialize Deserialize Pretty Print Config
Python json(内置) json.dumps(obj) json.loads(str) json.dumps(obj, indent=2) ensure_ascii, default, cls
JavaScript JSON(内置) JSON.stringify(obj) JSON.parse(str) JSON.stringify(obj, null, 2) replacer, space
Java Jackson / Gson mapper.writeValueAsString(obj) mapper.readValue(str, Class.class) mapper.writerWithDefaultPrettyPrinter() 注解、Module、Feature
Go encoding/json(内置) json.Marshal(obj) json.Unmarshal(data, &obj) json.MarshalIndent(obj, "", " ") struct tag
C# System.Text.Json(内置) JsonSerializer.Serialize(obj) JsonSerializer.Deserialize<T>(str) WriteIndented = true JsonSerializerOptions
TypeScript JSON(内置) JSON.stringify(obj) JSON.parse(str) JSON.stringify(obj, null, 2) replacer, space
Rust serde_json serde_json::to_string(&obj) serde_json::from_str::<T>(str) serde_json::to_string_pretty(&obj) serde attributes, custom (de)serializers

Frequently Asked Questions (FAQ)

How to handle datetime types in JSON serialization?

Most language JSON libraries do not support datetime types by default. The common approach is to serialize as ISO 8601 format strings (e.g. "2026-07-15T10:30:00Z") or Unix timestamps, then convert back to datetime objects during deserialization.

How to ignore null values or empty fields in JSON serialization?

Different languages have different implementations: Python can filter None values in custom encoders; JavaScript can use replacer functions; Java Jackson uses @JsonInclude annotations; Go uses omitempty struct tags; C# sets DefaultIgnoreCondition.

How to handle circular references in JSON serialization?

Circular references are a common pitfall. Standard JSON libraries usually throw exceptions or cause stack overflow. Solutions include using @JsonIdentityInfo (Java Jackson), implementing custom serializers, or converting to DTOs before serialization.

Why are my private fields not being serialized?

JSON serialization libraries typically only serialize public fields or properties with getters. This is a security consideration. You can use annotations like @JsonProperty (Java Jackson) or [JsonInclude] (C#) to include non-public members.

What are the performance optimization tips for JSON serialization?

1) Reuse serializer instances; 2) Use streaming APIs for large data; 3) Use compile-time generation (C# Source Generator, Go easyjson); 4) Avoid excessive nesting; 5) Use database pagination for large JSON.

Recommended Tools

These tools can help you handle JSON data more efficiently in General development:

Related Articles