Two different questions
"Validate this JSON" means one of two quite different things, and confusing them wastes a lot of time.
Syntax validation asks: is this well-formed JSON? Are the quotes double, the commas in the right places, the brackets balanced? This is what a parser does, and what a formatter does implicitly — it must parse before it can print.
Schema validation asks: is this the data I expected? Are the required fields present, are the types right, is age a non-negative integer, is email actually an email address?
A document can pass the first and fail the second completely:
{
"id": "not-a-number",
"email": "definitely not an email",
"age": -47
}
That is flawless JSON. It is also useless as a user record. Syntax validation will not tell you so.
Syntax validation
The fastest syntax check is to format the document: a formatter parses first, so malformed input produces an error rather than output.
In code, syntax validation is a try/catch around the parser:
function isValidJson(text) {
try { JSON.parse(text); return true; }
catch { return false; }
}
import json
def is_valid_json(text: str) -> bool:
try:
json.loads(text)
return True
except json.JSONDecodeError:
return False
jq empty file.json && echo "valid"
python -m json.tool file.json > /dev/null && echo "valid"
Validate against RFC 8259, the current Internet Standard, aligned with ECMA-404. It supersedes RFC 4627 and RFC 7159; the practical difference is that any well-formed value — not only objects and arrays — is valid at the top level.
Schema validation
JSON Schema is the standard vocabulary for describing the shape of a JSON document. A schema is itself JSON:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["id", "email"],
"additionalProperties": false,
"properties": {
"id": { "type": "integer", "minimum": 1 },
"email": { "type": "string", "format": "email" },
"age": { "type": "integer", "minimum": 0, "maximum": 150 },
"roles": {
"type": "array",
"items": { "enum": ["admin", "editor", "viewer"] },
"uniqueItems": true
}
}
}
That schema rejects the broken record above on three separate counts: id is not an integer, email fails the format check, and age is below the minimum.
additionalProperties: false is worth calling out — without it, a schema accepts any extra fields silently, which hides typos. "emial" passes happily unless you forbid unknown properties.
Validating against a schema in code
// Node — Ajv is the de facto standard validator
import Ajv from "ajv";
import addFormats from "ajv-formats";
const ajv = new Ajv({ allErrors: true });
addFormats(ajv); // "email", "date-time", "uri", ...
const validate = ajv.compile(schema);
if (!validate(data)) console.error(validate.errors);
from jsonschema import validate, ValidationError
try:
validate(instance=data, schema=schema)
except ValidationError as e:
print(f"{list(e.absolute_path)}: {e.message}")
Note allErrors: true in the Ajv example — by default it stops at the first failure, which makes for a frustrating debugging loop when several fields are wrong.
When schema validation is worth it
It has a real cost: a schema is another artefact to write, review, and keep in sync. It pays for itself when data crosses a boundary you do not control.
Worth it for: public API request bodies, third-party webhooks, user-supplied configuration files, CI checks on data files, and generating documentation or types from one source of truth.
Usually not worth it for: internal data you produce and consume within one service, one-off scripts, or anything where your language's own type system already covers you.
An honest middle ground for TypeScript projects: runtime validators such as Zod or Valibot give you validation and a static type from a single declaration, without the ceremony of a separate schema file. Use JSON Schema when the contract must be language-neutral and published; use a runtime validator when it only needs to hold inside one codebase.
Validation in CI
Catching malformed JSON before merge is cheap:
# Fail the build if any tracked JSON file is malformed
find . -name "*.json" -not -path "./node_modules/*" \
-exec sh -c 'jq empty "$1" || { echo "INVALID: $1"; exit 1; }' _ {} \;
For schema conformance, ajv-cli validates data files against a schema as a build step:
npx ajv validate -s schema.json -d "data/*.json" --all-errors
Add either as a pre-commit hook and malformed JSON stops reaching the repository at all.
A note on privacy
Validation requires reading the document, so where it happens matters. A client-side validator parses in your browser and transmits nothing. A server-side one uploads your document to a machine you do not control — which may log it.
If you are validating an API response containing tokens or personal data, use a tool that states clearly it is client-side. Our formatter runs entirely in the browser via the native JSON.parse; nothing is uploaded, logged, or stored. As general hygiene, still rotate any live credential that has passed through a third-party tool.
Related
- Common JSON Errors — what the syntax errors mean
- JSON Syntax Rules — the rules being validated
- JSON Best Practices — designing data worth validating