JavaScript guide

JSON Formatting Guide

The JSON syntax rules that trip developers up, how to pretty print and minify in JavaScript, PHP, Python and jq, and how JSON is used throughout WordPress.

Updated · 9 min read

JSON is the default format for APIs, configuration files, logs and data exchange. Its grammar is tiny, which is exactly why small mistakes — a trailing comma, a single quote — break parsers with unhelpful error messages. This guide covers the rules, the errors you'll actually see, and how to format JSON in the tools and languages web developers use every day.

The syntax rules (RFC 8259)

JSON is defined by RFC 8259 (and the identical ECMA-404). A JSON text is a single value, which may be:

  • an object: { "key": value, … } — keys must be double-quoted strings;
  • an array: [value, value, …];
  • a string in double quotes, with escapes such as \", \\, \n and \u00e9;
  • a number: decimal, optional minus sign, optional fraction and exponent — no leading zeros, no + prefix, no hex;
  • true, false or null, lowercase.

Whitespace between tokens is insignificant, which is what makes pretty printing and minifying lossless. JSON exchanged between systems must be UTF-8. Duplicate keys are technically allowed by the grammar but their behaviour is undefined — most parsers keep the last value — so avoid them.

{
  "id": 42,
  "title": "Hello \"world\"",
  "price": 19.99,
  "tags": ["json", "api"],
  "published": true,
  "author": null,
  "meta": { "views": 1.2e3 }
}

Common errors and how to fix them

Most invalid JSON comes from writing it like a JavaScript object literal. These are the usual suspects:

InvalidValidWhy
{"a": 1,}{"a": 1}Trailing commas are not allowed in objects or arrays.
{'a': 'b'}{"a": "b"}Strings and keys require double quotes.
{a: 1}{"a": 1}Keys must be quoted.
{"a": 1} // note{"a": 1}Comments are not part of JSON.
{"n": NaN}{"n": null}NaN, Infinity and undefined don't exist in JSON.
{"n": 007}{"n": 7}Leading zeros are invalid.
{"path": "C:\new"}{"path": "C:\\new"}Backslashes must be escaped; \n here is a newline, not a path.

Two more errors are not about syntax at all. A byte order mark or invisible character at the start of a file makes strict parsers fail at “position 0”. And an HTML error page served where JSON was expected produces the familiar Unexpected token '<' — check the HTTP status and response body before blaming the parser.

The JSON validator reports the line and column of the first error, which is much faster than counting characters from a stack trace.

Pretty print vs minify

Pretty-printed JSON adds indentation and line breaks for humans: code review, config files, debugging. Minified JSON removes all insignificant whitespace for machines: API responses, embedded data, storage. Both represent exactly the same data. Over HTTP with gzip or Brotli compression, the size difference is modest, so readability often wins for config files committed to Git.

JavaScript

const data = { name: "Utilo", tags: ["json", "api"], count: 3 };

JSON.stringify(data);            // minified: {"name":"Utilo","tags":["json","api"],"count":3}
JSON.stringify(data, null, 2);   // pretty, 2-space indent
JSON.stringify(data, null, "\t"); // pretty, tab indent

// The second argument filters keys or transforms values
JSON.stringify(data, ["name", "count"], 2);

// Re-format an existing JSON string
const pretty = JSON.stringify(JSON.parse(input), null, 2);

JSON.stringify silently drops undefined values and functions, converts NaN and Infinity to null, serialises Date objects as ISO strings, and throws on BigInt and circular references.

PHP

<?php
$data = array( 'url' => 'https://example.com/café', 'ok' => true );

echo json_encode( $data );
// {"url":"https:\/\/example.com\/caf\u00e9","ok":true}

echo json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
// {
//     "url": "https://example.com/café",
//     "ok": true
// }

// Fail loudly instead of returning false
$json = json_encode( $data, JSON_THROW_ON_ERROR );
$back = json_decode( $json, true, 512, JSON_THROW_ON_ERROR ); // true = associative arrays

By default PHP escapes forward slashes and non-ASCII characters. Both outputs are valid JSON; the flags just make it readable. JSON_PRETTY_PRINT always uses four spaces.

Python

import json

print(json.dumps(data, indent=2, ensure_ascii=False))
print(json.dumps(data, separators=(",", ":")))  # most compact output

# Pretty print a file from the shell
# python -m json.tool data.json

jq on the command line

jq . data.json                 # pretty print (2 spaces, colored)
jq -c . data.json              # minify to one line
jq --indent 4 . data.json      # custom indent
jq -S . data.json              # sort keys, handy before diffing
curl -s https://example.com/wp-json/wp/v2/posts | jq '.[].title.rendered'

For one-off jobs without a terminal, the JSON formatter and JSON minifier do the same in the browser, and the JSON viewer lets you collapse and explore deeply nested responses.

Formatting conventions for teams

When JSON files live in a repository, consistent formatting keeps diffs small and reviews readable. Agree on a few rules and let tooling enforce them:

  • Indentation: two spaces is the most common convention in the JavaScript ecosystem and is what npm writes to package.json. WordPress core's theme.json and block.json files use tabs, matching the WordPress coding standards. Pick one per project.
  • Trailing newline: end files with a single newline so Git doesn't report “No newline at end of file”.
  • Key order: keep logical order for hand-edited config; sort keys for generated files so regeneration produces stable diffs.
  • Automation: an .editorconfig file sets indentation per file type, and Prettier formats JSON files on save or in a pre-commit hook.

JSON in WordPress

The REST API

Every WordPress site exposes JSON at /wp-json/. Posts are at /wp-json/wp/v2/posts, and adding ?_fields=id,title,link trims the response to what you need. Adding ?_pretty returns indented output, which is useful when inspecting responses in the browser.

wp_json_encode()

Use wp_json_encode() instead of json_encode() in WordPress code. It accepts the same flags but also sanity-checks strings with invalid UTF-8, which json_encode() would reject. It is the right tool for passing PHP data to scripts:

<?php
wp_add_inline_script(
    'acme-app',
    'window.acmeSettings = ' . wp_json_encode( array(
        'restUrl' => esc_url_raw( rest_url( 'acme/v1/' ) ),
        'nonce'   => wp_create_nonce( 'wp_rest' ),
    ) ) . ';',
    'before'
);

AJAX responses

In admin-ajax.php handlers, wp_send_json_success() and wp_send_json_error() set the JSON content type, wrap your data in { "success": true, "data": … }, and end execution:

<?php
add_action( 'wp_ajax_acme_save', function () {
    check_ajax_referer( 'acme_save' );
    if ( ! current_user_can( 'edit_posts' ) ) {
        wp_send_json_error( array( 'message' => 'Not allowed' ), 403 );
    }
    wp_send_json_success( array( 'saved' => true ) );
} );

JSON also powers theme.json, block.json and ACF Local JSON — see the ACF field group guide. These files are strict JSON, so a trailing comma in theme.json silently disables all of its settings.

JSON inside HTML

Embedding JSON in a page — for JSON-LD structured data or initial app state — has one trap: a string containing </script> closes the script element early, which breaks the page and can open an XSS hole. Escape the angle brackets when the JSON goes inside <script>:

<?php
$schema = array(
    '@context' => 'https://schema.org',
    '@type'    => 'Article',
    'headline' => get_the_title(),
);
echo '<script type="application/ld+json">'
    . wp_json_encode( $schema, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE )
    . '</script>';

JSON_HEX_TAG encodes < and > as \u003C and \u003E, which parsers decode back to the original characters. In JavaScript, apply the equivalent replacement to the output of JSON.stringify before inserting it into server-rendered HTML. For building structured data, see the schema markup generator.

Validating JSON

  • Syntax validation answers “will this parse?” — JSON.parse in a try/catch, json_decode with JSON_THROW_ON_ERROR, jq empty file.json, or the JSON validator.
  • Schema validation answers “does it have the right shape?” using JSON Schema. Editors use the $schema property for autocomplete; WordPress publishes schemas for theme.json and block.json.
  • In PHP 8.3+, json_validate() checks syntax without building the decoded value, which saves memory on large inputs.

JSON vs JSONC vs JSON5

Because JSON has no comments, several supersets exist for configuration files:

  • JSONC (JSON with Comments) allows // and /* */ comments and, in some tools, trailing commas. VS Code settings and tsconfig.json use it.
  • JSON5 adds single quotes, unquoted keys, trailing commas, hex numbers, Infinity and multi-line strings.

Neither is valid JSON. Standard parsers — JSON.parse, json_decode, the WordPress REST API — will reject them. Use them only where the consuming tool explicitly supports them, and never for data exchange.

Working with large files

  • Parsing loads the whole document into memory; a 200 MB file can need several times that. Stream instead: jq --stream, Node stream parsers, or ijson in Python.
  • Prefer NDJSON (newline-delimited JSON, one object per line) for logs and exports. It can be appended to, processed line by line and split easily.
  • Large integers above 253 − 1 lose precision in JavaScript. APIs often send IDs as strings for this reason.
  • For spreadsheet work, flatten arrays of objects with the JSON to CSV converter.