JSON string escape & unescape
The escape module answers the daily question "what does this text look like as a JSON string?" — pasting a multi-line SQL query into a JSON API body, embedding an error message with quotes into a config value, or going the other way: turning the \n-and-\" soup from a log line back into readable text. Escaping converts real newlines to \n, tabs to \t, quotes to \", backslashes to \\, and control characters to \u00XX — exactly what JSON.stringify produces, because that's what runs underneath. Unescaping parses the input as a JSON string literal (quotes around it optional — the tool adds them if missing) and gives you back the raw text.
The most common gotcha is double escaping: if your output shows \\n where you expected a line break, the text was escaped twice — unescape twice to unwind. And when a "JSON string" fails to unescape, it's usually because it isn't one: single-quoted strings, or Python's repr() output with its ' quotes, look similar but aren't valid JSON.
Escape / unescape FAQ
Why does the escaped output show \uXXXX for some characters?
Control characters (below U+0020) have no literal form in JSON and must be written as \u00XX — the short names \n \t \r \b \f cover only five of them. Everything printable, including emoji and accents, stays literal because JSON is UTF-8: escaping non-ASCII is legal but unnecessary, and this tool doesn't inflate your string with it.
Do I need the surrounding quotes?
The quotes are what make it a string literal rather than raw text, so "wrap output in quotes" is on by default — paste-ready for a JSON document. Turn it off when you're inserting into an existing quoted context, like the middle of a template. When unescaping, the tool accepts input with or without the outer quotes.
JSONL ⇄ JSON array
JSON Lines (JSONL, also called NDJSON) is one complete JSON value per line — the format of log streams, ML training data, and bulk-export endpoints, because a consumer can process line by line without parsing the whole file. A JSON array is what most tools and APIs want instead. This module converts both directions: array → JSONL writes each element minified on its own line; JSONL → array parses each line and emits one array, pretty-printed or minified.
Conversion is line-based, which brings two honest limits: JSONL input must genuinely be one value per line (a pretty-printed object spanning multiple lines isn't JSONL — convert it to an array first with any formatter, or minify it), and everything is held in memory, so this is a tool for pasteable data, not multi-hundred-megabyte exports. Blank lines are skipped; a line that fails to parse stops conversion with its line number unless you tick "skip unparseable lines", which drops them and reports how many.
JSONL FAQ
Is JSONL the same as NDJSON?
For practical purposes yes — both mean newline-delimited JSON values. The two names come from parallel informal specs, and files in the wild use .jsonl, .ndjson, and even .json interchangeably. Everything this module accepts and produces works under either name.
Why did my pretty-printed JSON fail as JSONL input?
Because JSONL's whole definition is "one value per line," and pretty-printing spreads one value across many lines — the parser sees { as line one and fails. If you have a single pretty-printed array, you want the array → JSONL direction instead. If you have several pretty-printed objects concatenated, minify them to one-per-line first.
Flatten & unflatten
Flattening turns {"user":{"address":{"city":"Oslo"}}} into {"user.address.city":"Oslo"} — one level, path-style keys. It's the shape you need for spreadsheet and CSV export (columns can't nest), for diffing two configs key by key, for building translation-file keys, and for writing dotted paths into query languages and form field names. Unflattening is the exact inverse: paste dotted keys, get the nested structure back.
Arrays are where notation matters. Dot notation writes indices as plain segments (items.0.name); bracket notation writes items[0].name, which round-trips more safely because an index is syntactically distinct from a key that happens to be the string "0". On unflatten, numeric segments (or bracketed indices) become arrays, other segments become objects. The depth limit stops flattening at N levels, leaving deeper values as embedded objects — useful when you want top-level columns but not one column per leaf of a deep tree. One honest caveat: a key that itself contains a dot (legal in JSON) is ambiguous in dot notation once flattened; if your data has such keys, flatten with bracket notation or expect them to unflatten as nested paths.
Flatten FAQ
What happens to empty objects and arrays?
They're preserved as values: {"a":{}} flattens to {"a":{}} rather than disappearing — dropping them would make flatten→unflatten lossy, and this tool treats round-trip fidelity as the contract. The same goes for null, which stays null and is never confused with a missing key.
Why did unflattening create an object with keys "0", "1" instead of an array?
An array is only inferred when every segment at that level is a valid index counting from 0 without gaps. Keys like items.0 and items.2 with no items.1 — or a mix like items.0 and items.total — can't be an array without inventing or dropping data, so they become an object with those literal keys. Bracket notation input (items[0]) always signals array intent unambiguously.