A JSON formatter (formatter / beautifier / pretty printer / validator) is not decoration — it is readability infrastructure for API debugging, config file management, data exchange, and JWT payload decoding. Most developers’ formatting habit is “press the IDE format shortcut”, which solves 30% of the problem. The remaining 70% (trailing commas, Unicode escaping, large integer precision loss, BOM characters, single quotes, NaN / Infinity / undefined serialization, sort-keys side effects) cannot be solved by IDE.
A JSON formatter’s core value is not “making JSON look pretty” — it is making data human-readable during API debugging, config review, log analysis, JWT decoding. That’s infrastructure, not beautification. This article covers 8 real foot-guns in “symptom + why + how to fix” format, then introduces the Piick JSON Formatter — syntax highlighting + sort keys + tolerant parsing (JSON5 / single quotes / comments) + click-to-jump error positioning + 5-metric stats (keys / arrays / max nesting depth / bytes), 100% local, all done in 1 minute.
30-Second Overview
- JSON formatting != beautification: the former is readability infrastructure for API / config / log inspection; the latter is just “make the code look tidy”
- JSON standard has 4 hard constraints: trailing commas not allowed, strings must use double quotes, comments not allowed, NaN / Infinity / undefined not allowed (4 must-trip pitfalls)
- 3 common JS ecosystem misuses: trailing comma (legal in ES2017+), single-quote object literals, template strings nested in JSON (escaping disaster)
- 8 common foot-guns: trailing comma / Unicode escaping / large integer precision / indentation != nesting / BOM characters / single quotes / NaN-Infinity-undefined serialization / sort keys side effect
- Fix direction: always validate with a tool (especially production API config), always tolerant parse (development), always preserve large integers as strings (Snowflake ID), never auto-sort keys (affects Git diff)
- Use the Piick JSON Formatter online for formatting / validating / beautifying, tolerant parsing + syntax highlighting + click-to-jump error positioning + 5-metric stats, all processed locally
8 Common Foot-Guns
Foot-Gun 1: Trailing Comma Rejected by Standard JSON
Symptom: From Chrome DevTools “Copy as fetch”, copied response body, paste into JSON formatter reports “Unexpected token”. You think it’s a tool bug — actually DevTools’ copied JSON has trailing comma (extra comma after the last key), which standard JSON does not allow.
Why: ECMAScript 2017+ allows trailing comma in object literals, so Chrome / Firefox DevTools copied JSON often includes a trailing comma. Python 3.7+ dictionaries also allow it. JSON spec RFC 8259 section 5 clearly states “trailing commas are not allowed”. Naive JSON.parse strictly follows the spec, sees {"a": 1,} and reports error.
Fix: Production JSON must have no trailing comma. Development can use tool’s “tolerant parsing” option (Piick JSON Formatter supports JSON5 style, accepting trailing comma + single quotes + comments), but must be linted before commit to API / config files. CI runs prettier --check or eslint --rule no-trailing-commas as a safety net.
Real scenario: Frontend fetches backend API, Chrome DevTools “Copy as fetch”, paste to jq .response.users[0].id, errors out. Reason: DevTools output has trailing comma, jq is strict parse. Always lint before jq, or directly use jq tolerant mode jq -R ..
Foot-Gun 2: Unicode Characters Not Escaped
Symptom: Backend returns {"name":"张三"}, frontend JSON.parse works fine. But sometimes the same data pasted into a tool reports “Unexpected token”, or some libraries’ stringify output has Chinese becoming ?. This is inconsistent Unicode escaping.
Why: JSON spec states non-ASCII characters in strings can appear directly as UTF-8 (because JSON document itself requires UTF-8), and can also be escaped as \uXXXX. Both formats are legal. But some libraries’ default behavior differs:
- Python
json.dumpsdefaults toensure_ascii=True, escaping all non-ASCII to\uXXXX - JavaScript
JSON.stringifydefaults to preserving original Unicode - Java
ObjectMapperdefaults to escaping - Go
encoding/jsondefaults to escaping
Backend Python outputs {"name":"\u5f20\u4e09"}, frontend JS parses to 张三, but you copy backend raw response to vim to share with colleague, colleague sees {"name":"\u5f20\u4e09"} confused. This is UTF-8 vs \u escaping inconsistency.
Fix: Always ensure production JSON is UTF-8. Python backend uses json.dumps(..., ensure_ascii=False) for readable format (with Content-Type: application/json; charset=utf-8). When debugging, use tool’s tolerant parsing, treating \uXXXX as legal input.
Real scenario: Python backend dumps {"msg":"\u4f60\u597d"}, frontend fetch and response.json() gets {msg:"你好"}, correct. But you copy backend raw response to vim, give to colleague, colleague sees {"msg":"\u4f60\u597d"} confused. This is UTF-8 vs \u escaping inconsistency.
Foot-Gun 3: Large Integer Precision Loss (Snowflake ID Last Bits Become 0)
Symptom: Backend returns {"user_id": 1234567890123456789}, frontend JSON.parse makes user_id 1234567890123456800, last 3 digits are all 0. Backend’s ID is correct, frontend’s extracted ID is wrong, database query 404s.
Why: JavaScript Number is IEEE 754 double-precision float, safe integer range is 2^53 − 1 = 9007199254740991 (about 9 × 10^15). Integers exceeding this range lose precision. Snowflake ID is 19-digit integer (about 10^18), JSON.parse directly loses precision. Python int / Java long don’t have this problem, but JS is the most common frontend language.
Fix:
- Backend: serialize
bigintas string ("1234567890123456789"), not number - Frontend: use
json-bigintlibrary when parsing, or handle all as strings - Display: when displaying UI,
BigInt(id).toString(), when calculating usebiginttype - API design: when returning ID, consider UUID (36 chars) or ULID (26 chars), both string format
When validating API response with the Piick JSON Formatter, first check the placeholder, paste and verify output matches; if ID length > 15 digits, be alert for large integer precision issues.
Real scenario: Twitter Snowflake ID is 64-bit integer, about 1770000000000000000, far exceeding 2^53. Frontend fetches tweet id 1234567890123456789, actually gets 1234567890123458000, can’t find. Snowflake ID must be serialized as string.
Foot-Gun 4: Indentation != Nesting Structure
Symptom: Your code uses 2-space indentation, colleague’s uses 4-space indentation, PR review looks like completely different JSON, but parsed result is exactly the same. You think it’s different nesting depth, actually {"a":{"b":{"c":1}}} and {"a": { "b": { "c": 1 } }} are the same object.
Why: JSON syntax doesn’t care about whitespace, as long as RFC 8259 lexical rules are met, any number of spaces / tabs / newlines is equivalent. Indentation is just “human eye readability”, has nothing to do with actual nesting. This is also why JSON files in git diff look like the whole thing changed when adding/deleting a single field (because indentation rearranges).
Fix: Unify indentation style (recommend 2 spaces, saves line width) + use prettier / jq for auto-formatting, avoid manual formatting. Use tool’s “indent” option (Piick JSON Formatter supports 2 spaces / 4 spaces / tab / minify), team unified.
Real scenario: JSON config with 100 fields, 2-space vs 4-space:
- 2 spaces:
{"a":1,"b":2,"c":{"d":3}}compact - 4 spaces:
{"a": 1, "b": 2, "c": { "d": 3 }}loose
5+ nesting depth, 4 spaces each level, 5 levels = 20 char left margin, 80-char screen has only 60 char content. 2 spaces better for deep nesting.
Foot-Gun 5: BOM Characters Ignored
Symptom: JSON file looks normal in VSCode, paste to tool / Node.js JSON.parse errors “Unexpected token”. Right-click → View Encoding → UTF-8 with BOM. Reason: file has invisible character \ufeff (BOM, byte order mark) at the beginning.
Why: Windows Notepad / some old editors save UTF-8 files with BOM by default (3 bytes EF BB BF). JS / Python / most JSON parsers see \ufeff as JSON token and report error. JSON spec does not allow BOM in JSON text (except inside JSON document strings, as escape character).
Fix:
- Save without BOM: VSCode bottom-right → UTF-8 with BOM → UTF-8 (default)
- Command-line transcoding:
sed '1s/^\xef\xbb\xbf//' input.json > output.json - Python reading: use
encoding='utf-8-sig'to auto-strip - Node.js: use
fs.readFileSync(path, 'utf-8').replace(/^\ufeff/, '')
When using Piick JSON Formatter, paste JSON with BOM reports “Unexpected token”, manually delete BOM or use sed pre-process.
Real scenario: Windows colleague writes JSON config → saves → commits → CI runs JSON.parse → errors. CI doesn’t auto-strip BOM, need to change editor settings at source. Add insert_final_newline = true and “encoding UTF-8 no BOM” in .editorconfig, team unified.
Foot-Gun 6: Single Quotes Treated as JSON
Symptom: Python dict {'a': 1, 'b': 2} directly pasted into JSON formatter reports error. Python dict default output format looks like JSON, but uses single quotes, which is not legal JSON.
Why: JSON spec clearly states strings must use double quotes, single quotes don’t work. Python dict repr() outputs single quotes, JSON string outputs double quotes:
Newcomers easily treat print(d) as JSON, paste into tool and report error.
Fix:
- When debugging Python, use
print(json.dumps(d, indent=2))instead ofprint(d) - Tool’s “tolerant parsing” option accepts single quotes (Piick JSON Formatter supports JSON5 style)
- Educate team “Python dict != JSON, Python str uses single quotes, JSON uses double quotes”
Real scenario: Python newbie debugging API response, print(response) outputs {'code': 200, 'msg': 'ok'}, thinks it’s JSON, copies to backend colleague, backend colleague’s JSON.parse errors. Newbie’s first debug trap.
Foot-Gun 7: NaN / Infinity / undefined Serialization
Symptom: JS object {a: NaN, b: Infinity, c: undefined} serialized with JSON.stringify becomes {"a":null,"b":null} (NaN and Infinity become null), undefined fields disappear directly. If backend uses strict JSON parser (e.g., Python json.dumps then json.loads), behavior differs: Python json.dumps serializes float('nan') as NaN (non-standard), json.loads('NaN') reports error.
Why: JSON spec does not allow NaN / Infinity / undefined, but JS is the daily-use language for these:
NaN/Infinity: are JS global constants, produced on numeric calculation errorsundefined: JS “variable undefined” flagJSON.stringify: treats them all asnull(undefined directly drops fields)JSON.parse('NaN'): JS is lenient, accepts asNaN(non-standard)json.dumps(float('nan')): Python outputsNaN(non-standard)json.loads('NaN'): Python defaults strict, reports error
When exchanging data across languages, NaN / Infinity / undefined serialization behavior is inconsistent, bugs are easy.
Fix:
- API spec: never use NaN / Infinity, use
nullor omit for invalid values - Data cleaning: before serializing, manually
if (x === undefined || Number.isNaN(x)) x = null - Backend strict validation: Python
json.dumps(..., allow_nan=False), fail-fast on error - Library: JSON Schema validation at API entry, reject NaN / Infinity
When using Piick JSON Formatter, paste JSON with NaN / Infinity reports “Unexpected token n” (because NaN is not a legal token), manually replace with null.
Real scenario: Frontend fetches backend API, response {score: NaN} (backend bug), frontend JSON.parse gets score: NaN, SQL query WHERE score > 0 returns no rows (NaN > 0 is false), UI shows “no data”, debugging half a day to find it’s NaN. Never pass NaN, backend does defensive validation.
Foot-Gun 8: Sort Keys Alter Git Diff / Language Semantics
Symptom: You turn on tool’s “sort keys”, JSON looks tidy, git commit later colleagues review see 100 lines of diff, all field order changes, no actual content change. Or more serious: Go map / Python 3.7+ dict in some edge cases are sensitive to key order, semantics change after sorting.
Why: JSON object is unordered map by spec definition, but RFC 8259 actually allows implementations to preserve order (because JS object literals, Python 3.7+ dict are ordered). Tool’s sort keys option is for display tidiness, but:
- Git diff noise, affects review quality
- JSON Canonicalization Scheme protocols sensitive to key order fail
- Some libraries’
Object.keys(obj)order depends on insertion order, after sortingforEachorder changes
Fix: Production JSON don’t auto-sort keys. During development use tool to view structure, before commit use jq or prettier to reformat (preserve original order). If team really needs unified order, use lint to enforce (sort-package-json for package.json), don’t rely on tool runtime sorting.
When using Piick JSON Formatter, default sort keys off, only manually turn on when “I want to see structure”.
Real scenario: Colleague turns on sort keys, commits 1000-line package.json all rearranged, reviewer looks at diff sees 800 lines are order changes, 200 lines are new dependencies, 2 hours review all wasted. Don’t use sort keys on production config.
Tool Selection Decisions
Option A: IDE Built-in Formatting (VSCode / WebStorm)
Best for: Writing JSON daily, already using an IDE.
Pros: Real-time formatting, don’t leave editor, integrate with schema (auto-complete keys). Cons: Local JSON only, can’t sync across tools (IDE format differs from jq format), no batch processing (CI auto-format needs separate setup).
Option B: Command-Line Tools (jq / python -m json.tool)
Best for: Auto-formatting in CI / build pipeline, projects with shell environment.
Pros: Scriptable, batchable, combined with lint, CI integration. Cons: Not intuitive (text output without colors), no syntax highlight, no tolerant parsing (JSON5 / single quotes / comments).
Option C: Piick JSON Formatter
Best for: Cross-tool debugging (API response + config file + JWT payload), don’t want extra npm package (pure frontend project), sensitive data (production API config / JSON with PII) don’t want any third-party server.
Pros: Zero dependency (pure frontend JSON.parse + tokenizer), syntax highlighting + sort keys + tolerant parsing (JSON5 / single quotes / comments) + click-to-jump error positioning + 5-metric stats (keys / arrays / max nesting depth / bytes), data local. Cons: Can’t integrate into CI (use Option B to supplement), can’t auto-save (pure frontend tool).
Decision tree:
- Already writing JSON → Option A (IDE plugin)
- CI integration + shell environment → Option B (jq)
- Cross-tool debugging + sensitive data + don’t install anything → Option C (Piick)
5 Real-World Scenarios
Scenario 1: API Response Debugging
Frontend calls backend API, response is compressed minified JSON, paste into tool to auto-beautify + fold / expand + show stats (keys / arrays / depth).
Steps:
- fetch API → response.text() → paste into Piick JSON Formatter
- Auto syntax highlight (5 token types)
- Check top stats:
{keys: 47, arrays: 12, maxDepth: 4, bytes: 1024} - Error position click-to-jump: JSON.parse errors, click error banner, jump to failing character position
- Copy formatted result / fold nesting levels
Real suggestion: Always use tool to view API response, don’t directly view minified JSON in console.log, human eye can’t read it.
Scenario 2: package.json / Config File Validation
When writing package.json, typo (extra comma / missing right bracket / unclosed string), CI npm install errors, but editor doesn’t error (IDE tolerant). Run validation with tool.
Steps:
- Copy package.json content → paste into tool
- Tool reports error → see “Line X, column Y” error position → click jump → fix
- Especially check:
"scripts","dependencies","devDependencies"three places (most likely to typo) - Before commit run
npm install --dry-runto double-confirm
Real suggestion: Use Piick JSON Formatter’s “tolerant parsing” option (tolerant) to see “syntax error but close to correct” hint during development, friendlier than strict parser.
Scenario 3: JWT Payload Decoding
JWT is base64url-encoded JSON, header.payload.signature three parts. When decoding payload to see claims, first use Piick JWT Decoder (same doc cluster) to split base64, then use JSON Formatter to verify decoded JSON is legal.
Steps:
- JWT
eyJhbG...sw5c - Piick JWT Decoder split three parts
- payload is
{"sub":"1234567890","name":"John Doe","iat":1516239022}← minified JSON - Paste into Piick JSON Formatter beautify + see timestamp fields (
iat/exp/nbfto readable time) - Check if
expis expired
Real suggestion: JWT debug chain: JWT Decoder → JSON Formatter → Unix Timestamp Converter (same doc cluster), 3-piece set covers complete flow.
Scenario 4: Snowflake ID / Large Integer ID Serialization
Backend uses int64 to return ID, frontend fetch and id.toString() last bits become 0. String-handling.
Steps:
- Backend Python
json.dumps({"id": int(2**60)})→ output{"id": 1152921504606846976}← last bits may lose precision - Fix: backend
json.dumps({"id": str(2**60)})→ output{"id": "1152921504606846976"}← string, frontend handles as string - Frontend
BigInt(id).toString()for math operations - UI displays
id.slice(0, 4) + "..." + id.slice(-4)(like bank card number display)
Real suggestion: API spec: large integer IDs always use string. Frontend never assume ID is Number.
Scenario 5: i18n Resource Files / Multilingual Dictionaries
i18n JSON files contain Chinese / Japanese / emoji, correctly handle Unicode escaping. Development use tool for visualization, production use UTF-8 + ensure_ascii=False output readable format.
Steps:
- i18n file
zh.json:{"hello":"你好,世界!🌏"}← direct UTF-8 - Paste into Piick JSON Formatter verify UTF-8 characters display normally
- Backend output uses
json.dumps(..., ensure_ascii=False)(Python) orJSON.stringify(JS default) - Response header:
Content-Type: application/json; charset=utf-8 - CI adds lint:
eslint-plugin-unicornor custom script checksensure_asciiconfig
Real suggestion: Never write \uXXXX escape in JSON strings, development period directly write readable characters with UTF-8. Production period let framework / library decide how to serialize, configure ensure_ascii=False yourself.
Recommended Practices
- Always validate JSON with tool (especially production API config), don’t rely on IDE tolerance
- Always tolerant parse during development (tool’s tolerant mode), production lint no trailing comma + no comments + no single quotes
- Large integer IDs always serialize as string (Snowflake ID / distributed primary key), don’t assume frontend Number safe
- Don’t auto-sort keys (affects Git diff and key-order-sensitive languages), only manually turn on when “viewing structure”
- Unify indentation (recommend 2 spaces) + use prettier / jq for auto-formatting, avoid manual formatting
- Always use UTF-8 + Content-Type charset=utf-8, don’t use
\uXXXXescape (development readability poor) - CI runs JSON Schema validation, at API entry reject NaN / Infinity / strict mode field missing
- Windows editor close BOM,
.editorconfigteam unified, avoid BOM character erroring in CI - Use the Piick JSON Formatter online for formatting / validating / beautifying, syntax highlighting + tolerant parsing + click-to-jump error positioning + 5-metric stats + data local, no upload. Combined with Piick JWT Decoder split base64, Piick SQL Formatter format SQL, Piick URL Encoder/Decoder handle URL params, Piick Cron Generator generate cron expressions, Piick Regex Tester test JSON field value regex — 5-piece set covers all JSON upstream / downstream dev scenarios
JSON formatting is not beautification, it is readability infrastructure for API / config / log. The truly “correct format” = standard JSON + UTF-8 + large integers as strings + no sort + no comments + no trailing comma + team unified indentation. Bookmark the Piick JSON Formatter — when debugging API / validating config / parsing JWT payload, open it, 1 minute, daily dev efficiency boost 30%.