Cookies for analytics and advertising
We use cookies for analytics and advertising, both sent to Google. Refusing changes nothing you can see.Read the privacy page
Converting Parquet to JSON gives you one indented array with a row per object, which is what a fixture, a request body or a JSON viewer expects. Two things about the result are worth knowing first: nested columns arrive as strings of JSON rather than as structure, and a Parquet file expands enormously on the way into text.
Up to 100 files at once. Mixed formats are fine.
They convert one after another and download together as a ZIP.
Parquet to JSON
Parquet is what a data platform hands out and JSON is what everything else eats. The gap is where this conversion lives: a fixture that has to sit in a repository next to the test that reads it, a seed file for a local mock service, a body to paste into a request while you work out why an endpoint rejects it, or fifty rows you want to look at in a JSON viewer without installing DuckDB.
Note that the interesting cases are all small. Parquet exists to make very large tables cheap to query, and none of that survives the trip — the columnar layout, the compression, the statistics that let a reader skip whole row groups. What you get is a text document, and a text document is the right artefact for a hundred rows and the wrong one for ten million.
The Parquet file is transposed back into rows, and those rows are written as a top-level JSON array with two-space indentation and a trailing newline. Column names become object keys exactly as the schema spells them, including any that a JavaScript identifier could not be.
There is no envelope and no metadata. If the destination expects the rows under a name — `{"rows": […]}` is the usual shape — add it yourself; `jq '{rows: .}'` does it in one step. The array length is the row count, so it is also the quickest sanity check that the extract contains what you were told it contains.
This is the surprise on this page and it is worth reading twice. Parquet has real nested types — LIST, STRUCT and MAP — and the reader here hands them back as JavaScript objects, which are then serialised to text and placed in the field as a string. A `tags` column holding two values does not become `["a","b"]` in the output. It becomes `"[\"a\",\"b\"]"`, quoted, with the inner quotation marks escaped.
Nothing is lost and everything is one step out of reach. `jq '.[0].tags[0]'` returns an error about a string having no indices; `jq '.[0].tags | fromjson | .[0]'` returns the value. If the JSON is going into a test fixture, the honest move is to unwrap those columns once with `jq 'map(.tags |= fromjson)'` and commit the structured version, rather than leaving a double encoding for whoever reads the fixture next to discover.
A Parquet timestamp column is a typed integer with a unit and a time-zone flag recorded in the footer. JSON has no date type at all, so each value is written as an ISO 8601 string — `2026-03-01T08:00:00.000Z` — which is unambiguous, sorts lexicographically in the right order, and is understood by `Date.parse`, `datetime.fromisoformat` and every JSON schema `format: date-time` check.
What is gone is the declaration. The file no longer says that the column is a timestamp; it says the column is a string that happens to look like one. Anything downstream has to be told again — a `parse_dates` argument, a schema, a cast. For a fixture that is fine and explicit. For a load it is one more place to get the time zone wrong.
Parquet has a 64-bit integer type and JavaScript numbers carry 53 bits of integer precision. Rather than silently rounding, a value that does not fit is written as a quoted string; one that does fit is written as a bare number.
The trade is deliberate and it is the right way round — an order number that comes back off by one is a bug nobody finds until it matters, whereas a column that is sometimes a number and sometimes a string is at least visible. It does mean a column can change type partway down the file, which will make a strict JSON schema unhappy. If that is your destination, cast the whole column to text in the query that produced the extract and the JSON will be consistent.
A Parquet `BYTE_ARRAY` without a UTF8 annotation is bytes rather than text, and bytes have no faithful representation in JSON. Each such value is written as a lowercase hex string — two characters per byte, no separator, no prefix.
Hex is reversible and obviously not prose, which is the reason for choosing it over an attempt at decoding. It is also twice the size of the bytes it encodes and half the size of nothing useful, so if a binary column is large and you do not need it, drop it in the query rather than carrying it through the conversion. A hash column is fine; an embedded thumbnail column is not.
A missing value in Parquet becomes `null` in the JSON, distinct from an empty string and distinct from a zero. That sounds unremarkable until you compare it with the delimited targets, where both null and empty collapse into nothing between two separators and no query afterwards can tell them apart.
For a fixture that difference is the whole point. A test that asserts on the absence of a value needs the absence to be representable, and JSON is the only text target here that represents it. It is also why a round trip through JSON preserves more of a Parquet file than a round trip through CSV, even though neither carries the schema.
A great deal, and from three directions at once. Parquet stores each column once with its type declared, then dictionary-encodes repeated values and compresses the pages; JSON repeats every column name on every row, writes every number as decimal digits, and this output indents each field onto its own line.
Plan for an order of magnitude rather than a percentage, and more when the table is wide or the values repeat heavily — a status column of five distinct strings costs almost nothing in Parquet and its full length on every row in JSON. The whole document is also assembled in memory as one string before it can be saved, which is the practical ceiling well before the 100 MB free limit. Take a `LIMIT` on the extract rather than converting a warehouse table and hoping.
The footer that made the file self-describing does not come through. Column names survive as keys and nothing else does: not the declared types, not the nullability, not the compression, not the row-group statistics, not the key-value metadata a producer may have written into it.
If you have DuckDB, `DESCRIBE SELECT * FROM 'file.parquet'` prints the schema in one line of shell and takes ten seconds, and knowing which columns were INT64 rather than DOUBLE explains most of what looks odd in the JSON afterwards. If you do not, convert a handful of rows first and read them — the quoted integers, the ISO strings and the escaped nested columns each tell you what the source type was.
In this tab. A Parquet reader written in plain JavaScript is fetched the first time you use one of these pairs and then does its work locally; the file itself is never sent anywhere, and the network tab during a conversion shows the reader arriving and nothing leaving.
That distinction matters for this format more than most, because Parquet is a warehouse artefact. Files with this extension are extracts from production tables — customers, orders, events, salaries — and they arrive by way of a data team who would have to report an upload. There is nothing to report here.
| Parquet | JSON | |
|---|---|---|
| Full name | Apache Parquet | JavaScript Object Notation |
| File extension | .parquet | .json |
| Media type | application/vnd.apache.parquet | application/json |
| Compression | Lossless — nothing is discarded | — |
| First published | 2013 | 2001 |
| Published by | Apache Software Foundation | — |
| Specification | — | RFC 8259 |
| Licensing | Open standard | Open standard |
| Standing today | Current | Current |
| Opens in a browser | No browser | Every browser |
| Considered instead | CSV | XML, YAML, NDJSON |
Nothing is discarded. Parquet and JSON both store their content losslessly, so the conversion is a change of packaging rather than a change of quality, and it can be repeated without accumulating damage.
JSON is a working format and Parquet is a finished one. What comes back is editable text and objects rather than a picture of a page, which is usually the reason for the conversion and also where its limits are.
JSON opens in every current browser. Parquet has narrower browser support than that. If the file is going onto a web page or into a form, that is usually the whole reason for the conversion.
The usual programs do not overlap: Parquet opens in pandas, Apache Spark and DuckDB, JSON in Visual Studio Code, jq and Postman — so whoever receives the result needs something from the second list.
JSON dates from 2001, specified as RFC 8259. Visual Studio Code, jq and Postman all read it.
No. This conversion runs entirely inside your browser, so the file never leaves your device. You can confirm it yourself: open the network tab of your browser's developer tools and convert something. You will see the page load, plus the analytics and advertising the site is paid for with — and nothing carrying your file. The engine behind this particular pair is parquet-wasm, a WebAssembly build of the Apache Arrow reader; your browser fetches it once and caches it.
A top-level array with one object per row, indented two spaces, keys named after the Parquet columns. There is no wrapper object and no metadata block — if you need the records under a named key, add it afterwards.
Because a Parquet LIST, STRUCT or MAP is serialised to JSON text and placed in the field as a string. The value is intact but it is quoted, so jq sees a string. Piping it through fromjson turns it back into structure.
They become ISO 8601 strings, because JSON has no date type. The Parquet column knew it held a timestamp and the JSON does not, so whatever reads the file has to be told again.
Yes, and they change type to stay safe. A 64-bit integer that does not fit in a JavaScript number is written as a quoted string rather than rounded, so an order number past nine quadrillion keeps every digit and gains quotation marks.
The free ceiling is 100 MB, and the real limit arrives sooner: the rows are decoded into memory and the whole JSON document is built as one string. Parquet compresses hard, so a modest file expands into a great deal of text.
No. A JavaScript Parquet reader is fetched the first time you convert one, and the file itself is decoded in this tab. The extract goes nowhere, which is the point when it came out of a production warehouse.