"JSON tools" sounds like one category. In practice it's about eight neighboring problems that share a data format. Formatting a blob so you can read it is a different job from checking that it parses. Diffing two payloads is different again, and so are converting between formats, generating a schema, generating mock data from that schema, and going the other way: inferring a type from real samples instead of writing a schema by hand. This page lays out each of those, what separates it from the others, and which tool or guide on this site covers it.
What JSON refuses to do
JSON was designed as a strict, unambiguous subset of JavaScript object literals, and it carries a short list of things it won't support. There are no comments. A // note inside a JSON document is a parse error, not a warning. There are no trailing commas after the last item in an array or object, and keys must be quoted. The syntax doesn't distinguish integers from floats, and there's no date type: dates are strings, usually ISO 8601 by convention, with nothing in the format enforcing it.
None of that is an oversight. It's the trade that lets every language ship a small, fast, dependency-free JSON parser, instead of implementing the looser and more ambiguous grammar that YAML allows. Most of the tools below exist to deal with some consequence of that strictness.
Formatting and validation answer different questions
A formatter and a validator often live in the same tool, but they do different jobs. Formatting re-indents and re-spaces a document so a person can read it. It has to work on broken input too, because the moment you most need a formatter is when something is wrong and you're trying to find it. Validation checks whether the document parses as JSON at all: matched brackets, quoted keys, no trailing commas. That comes before the separate question of whether the data has the shape you expect, which is what a schema checks (more on that below).
The JSON Formatter does both. It pretty-prints valid JSON, and when the input doesn't parse it points at where parsing stopped instead of just saying "invalid."
"Unexpected token in JSON at position 0" covers several different bugs
This is probably the JSON error people search for most, and the message doesn't tell you which of its causes you've hit. Position 0 almost always means the parser received something that wasn't JSON before it read a single character of data. Common culprits: an empty response body, an HTML error page served with a 200 status, a value that's already a JavaScript object being passed to JSON.parse a second time, or a byte-order mark (BOM) an editor quietly added to the start of the file. The full guide walks through each cause and shows what the response body looks like in each case, since the error message leaves that out.
Double-encoding is a related failure. It happens when a value goes through JSON.stringify twice, usually because one layer of a system serializes a value and hands it to another layer that serializes it again. What you end up with is a JSON string whose value is itself JSON-encoded, escaped quotes and all, where you expected an object:
// what you have — a string, once you JSON.parse it
"{\"id\":1}"
// what you expected — an object
{"id":1}Parsing it once succeeds and gives you a plain string. Nothing throws, so the bug shows up a step later as "why is this a string?" The double-encoding guide and the JSON String Unescape / Escape tool are both for unwinding this.
Schema generation, and what "required" means when it comes from samples
The JSON Schema Generator takes sample JSON and produces a draft-07 schema. The thing to understand about generating a schema from samples, rather than designing one by hand, is what "required" means when it's inferred. A field that shows up in every sample you pasted will look required. But "present in the samples I have" and "always present in the live API" are two different claims, and a generator can only make the first one. The guide on this shows how adding more samples, especially edge cases like an empty list or an omitted optional field, changes the inferred schema, sometimes a lot.
JSON Schema drafts: what draft-07 covers and what it doesn't
JSON Schema isn't one fixed spec. It has gone through numbered drafts (04, 06, 07) and then year-named ones (2019-09 and 2020-12), and they aren't all compatible with each other. The Schema Generator here targets draft-07 and says so in the schema's own $schema field (http://json-schema.org/draft-07/schema#). That's a deliberate choice. Draft-07 is old enough that validator libraries in nearly every language support it, and a lot of existing tooling, including much of the OpenAPI 3.0 ecosystem, still consumes it.
The 2020-12 draft adds things draft-07 lacks. prefixItems validates a fixed-position tuple (draft-07 overloads a plain array under items for this, which is less clear). unevaluatedProperties/unevaluatedItems let you reason about what a combination of allOf/if/then subschemas has already covered. And $dynamicRef/$dynamicAnchor give a clearer version of the recursive-schema pattern that draft-07's $recursiveRef handled more implicitly. The common case this tool targets, inferring a plain object/array/property shape from real samples, needs none of that, so draft-07 is a reasonable default rather than a limitation you have to work around.
One caveat if you're about to feed the output into strict tooling: the generator produces a practical subset of draft-07 (type, required, properties, array items), not every combinator the spec defines. It doesn't emit oneOf/anyOf/allOf, conditional if/then, or format assertions. Validating input against a schema is also done by this tool's own structural logic, not against the official draft-07 meta-schema. That covers the shape-from-samples job. If you're authoring a schema that needs the full combinator vocabulary, treat the output as a starting point to extend by hand.
JSON Schema vs Zod depends on where the schema has to go
Once you have a schema, the right validator depends on where that schema needs to live. JSON Schema is a portable document. Any language with a conforming validator can check data against it, so it fits when the schema has to leave a single codebase: an OpenAPI spec, a consumer that isn't written in TypeScript, a form-generation library. Zod is TypeScript code. One Zod definition gives you a runtime validator and a static type, but only code running in a JS/TS runtime can use it. The JSON Schema vs Zod guide goes into when to pick which. Neither is better in general; the question is how far the schema has to travel.
Textual and semantic diffs
A plain text diff treats two JSON files as lines of text. Reformat one of them, with different indentation or reordered keys, and the diff shows a total rewrite even though the data is the same. A structural, JSON-aware diff compares the parsed data instead, so reordered keys and whitespace changes produce no diff and a changed value does.
Arrays are where this gets subtle. Object key order means nothing in JSON, but array order does: [1, 2, 3] and [3, 2, 1] are different values. A diff tool has to treat those two cases differently instead of ignoring order everywhere. The JSON Diff Viewer does the structural comparison. The semantic-vs-textual diff guide covers the details, including type coercion (a number that quietly became a string), one of the most common bugs a semantic diff catches and a naive one misses.
Converting to and from CSV, YAML, and XML
Conversions between JSON and CSV, YAML, or XML aren't symmetric, and the usual mistake is assuming a converter round-trips losslessly in every direction.
CSV is flat and untyped. When you convert CSV to JSON, every value starts as a string until the converter (or you) decides which columns should become numbers or booleans. CSV also has no way to represent a nested object or array, so JSON with nested structure has to be flattened before it can go back to CSV.
YAML is a superset of JSON in the sense that most valid JSON is valid YAML. But YAML adds type coercions JSON doesn't have: an unquoted yes or no can parse as a boolean, depending on the YAML version and the parser. The YAML ↔ JSON Converter has to make a decision about that.
XML is the least symmetric of the three. It has attributes and mixed text content, and no built-in way to say "this element repeats, so it's an array." A single occurrence of a repeatable element looks the same as a one-off element unless the converter is told which tags repeat. The XML ↔ JSON Converter makes a documented best-effort choice on each of these instead of pretending the conversion is lossless.
YAML also comes up as a config-file choice rather than a conversion target. The JSON vs YAML guide covers when YAML's comments and lighter syntax are worth its extra parsing ambiguity, and when JSON's strictness is safer for a file that machines write or read.
Generating mock data from a schema
Mock data runs the other way from validation: you start with a schema and produce fake data that satisfies it. That's useful for seeding a UI, a test fixture, or a demo before the API exists or when you can't reach it. The Mock / Fake JSON Data Generator takes the same kind of schema the JSON Schema Generator produces and fills it with values that match the declared types and constraints. Generation has its own questions. Should the same schema always produce the same data, or different data each run? And what happens with a constraint a naive generator can't satisfy by construction, such as a regex pattern on a string field?
When JSON doesn't fit in memory: streaming and JSON Lines
Everything above assumes JSON.parse can read the whole document into memory at once. That holds for most JSON you'll see (API responses, config files, small and medium exports) but not all of it. A single array with millions of elements, or a document in the hundreds of megabytes, can be slow or impractical to parse this way. That's especially true in a browser tab, which has tighter memory limits and a single main thread compared with a server-side batch job.
The tools on this site run entirely in your browser tab, by design. They're limited to what the browser and JSON.parse can hold in memory at once, and they don't stream. For very large data, the usual server-side answer is a streaming parser that emits values as bytes arrive instead of waiting for the whole document and building one in-memory tree.
A related format to know by name is JSON Lines (also written JSONL, or newline-delimited JSON). Instead of one big array wrapping every record, each line of the file is its own complete JSON value, with no enclosing [/] and no commas between records:
{"id": 1, "name": "Ada"}
{"id": 2, "name": "Grace"}
{"id": 3, "name": "Katherine"}That layout helps with the memory problem. A reader can process a line, discard it, and move on without holding the whole file, and a writer such as a log pipeline or export job can append a record without rewriting an enclosing array's brackets. That's why it shows up in log shipping, bulk export/import formats, and streaming ingestion pipelines.
JSON Lines is a separate format, not a variant of JSON syntax. A JSONL file isn't valid JSON: it has no single top-level value, and depending on the parser, no comma-separated array syntax at all. A tool built for JSON has to support JSON Lines on purpose; it won't work by accident.
From samples to a type
So far, every tool formats or validates a document you already have, or converts it. The last one skips the intermediate JSON Schema document and infers a TypeScript type or a Zod schema directly from real API response samples. The JSON to TypeScript and JSON to Zod tools use the same multi-sample inference engine. Paste more than one sample, and a field that's a string in one and null in another becomes a union, instead of the generator picking whichever type it saw first. A single-sample tool can't make that call; all it can describe is what one example looked like.
Live Types runs the same engine and adds one step. Alongside the inferred TypeScript type and Zod schema, you can save the inferred shape as a snapshot and later diff a fresh sample against it. That's the contract-drift check: does this API still return what my code assumes? It's a separate question from validation or type generation. A schema or type records what a response looked like when you generated it. A saved snapshot plus a later diff tells you whether that's still true. The breaking-changes guide covers which changes break consumers and which are safe additions.