How to validate JSON
There are three separate questions behind "is my JSON valid": does it parse, does it mean what you intended, and does it match the shape a consumer expects — and only the first is answered by a syntax check.
Level 1: does it parse?
The cheapest check, and the only one most tools perform. Either the grammar is satisfied or it is not.
# jq: silent on success, prints the position of the error on failure
jq empty data.json
# Python, no dependencies
python -m json.tool data.json > /dev/nulltry {
JSON.parse(text);
} catch (error) {
// The message includes the character offset in most engines
console.error(error.message);
}The five errors that account for most failures
| Error | Looks like | Fix |
|---|---|---|
| Trailing comma | { "a": 1, } | Remove the last comma |
| Single quotes | { 'a': 1 } | JSON requires double quotes |
| Unquoted key | { a: 1 } | Quote every key |
| Comment | // note | JSON has no comments — remove it |
| Unescaped control character | A literal newline inside a string | Escape it as \n |
Level 2: does it mean what you think?
A document can parse perfectly and still be wrong. The clearest example is a duplicate key.
{
"timeout": 30,
"retries": 3,
"timeout": 300
}
// Valid JSON. Almost every parser gives you 300.
// The 30 is gone, silently, with no error anywhere.The specification leaves this undefined rather than forbidding it, so no syntax check will flag it. Detecting it properly requires tracking object scopes — the same key appearing in two sibling objects is legal and extremely common, so a tool that searches the text for repeated strings will produce false positives on nearly every real document.
{
"users": [
{ "name": "Ada" },
{ "name": "Grace" }
]
}Level 3: does it match a schema?
JSON Schema describes the shape a document should have: which fields exist, what types they hold, which are required, what values are allowed. It is the only one of the three levels that can catch a field that is a string when it should be a number.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["name", "port"],
"properties": {
"name": { "type": "string", "minLength": 1 },
"port": { "type": "integer", "minimum": 1, "maximum": 65535 },
"debug": { "type": "boolean" },
"tags": { "type": "array", "items": { "type": "string" } }
},
"additionalProperties": false
}{
"name": "checkout",
"port": "8080",
"colour": "blue"
}
// port is a string, not an integer
// colour is not in the schema, and additionalProperties is false
// debug is absent, which is fine — it was not requiredWhich draft you are using matters
JSON Schema has several revisions — draft-04 through 2020-12 — and they are not interchangeable. A validator built for draft-07 given a 2020-12 schema typically fails with "no schema with key or ref", which reads like a broken reference rather than a version mismatch. Declare the draft with $schema, and use a validator that reads it.
npx ajv-cli validate -s schema.json -d data.json
# For a 2020-12 schema, the draft has to be selected explicitly:
npx ajv-cli --spec=draft2020 validate -s schema.json -d data.jsonGenerating a schema from data you already have
Writing a schema by hand for an existing payload is tedious. Inferring one from a sample gets you most of the way, and then you correct it — inference can see that a field is a string, but not that it must be a valid email, and it will mark every field it saw as required whether or not it is.
Validating in a build or CI
- name: Validate configuration
run: |
for file in config/*.json; do
jq empty "$file" || exit 1
done
npx ajv-cli validate -s schema.json -d "config/*.json"Validating configuration in CI is one of the highest-return checks available: it costs seconds and catches the class of failure that otherwise surfaces at deploy time, in production, on a Friday.
Validate JSON in your browserSyntax, duplicate keys and JSON Schema. Nothing uploaded.
Questions
How do I know if my JSON is valid?
Run it through a parser. jq empty file.json on the command line is silent on success and reports the position of the problem on failure; JSON.parse in JavaScript throws with the offset. An online validator that underlines the exact character is easier to act on than one that only reports a line number.
What is the most common JSON error?
A trailing comma — a comma after the last element of an object or array. JSON forbids it, and most languages that JSON resembles allow it, so it is a habit that transfers badly. Single quotes and unquoted keys are close behind.
Can JSON be valid but still wrong?
Yes, and it is common. A duplicate key parses fine and silently discards the earlier value. A field holding "8080" instead of 8080 parses fine and breaks whatever expects a number. Syntax validation cannot catch either; a schema catches the second, and scope-aware duplicate detection catches the first.
What is JSON Schema?
A vocabulary, itself written in JSON, for describing the shape a JSON document should have: required fields, types, value ranges, string patterns and nested structures. It turns "this looks right" into something a machine can check in CI.
Which JSON Schema draft should I use?
Draft 2020-12 for anything new — it is current and widely supported. Whichever you choose, declare it with $schema at the top of the document, because a validator assuming a different draft will fail in ways that do not look like a version problem.
How do I validate JSON against a schema online?
Open the document in fileviewer.dev, switch to the Schema view, and paste or generate a schema. Errors appear as squiggles in the source and as a list grouped by the keyword that failed. Drafts 06, 07, 2019-09 and 2020-12 are all supported, selected from your document’s $schema, and nothing is uploaded.