Paste a JSON array and read it as a table instead of
scrolling through braces. Each object becomes a row and each top-level key a column. A record missing a field shows up as an empty cell. Nested objects are flattened into dotted columns such as user.name, and arrays appear as JSON text inside their cell.
When the table looks right, take it with you: Copy for Email
puts a bordered table on the clipboard that pastes into Gmail, Outlook,
Google Docs, Word or Notion as a real table, Copy HTML
gives clean <table> markup for a web page, and
Copy Markdown gives a pipe table for a README, a GitHub
issue or a Slack message.
[
{
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"role": "Admin"
},
{
"id": 2,
"name": "Jane Smith",
"email": "jane@example.com",
"role": "User"
}
] Data rendered as responsive HTML table
| id | name | role |
|---|---|---|
| 1 | John | Admin |
| 2 | Jane | User |
Records that lack a key show an empty cell. A gap in a column is easier to see than a missing line in a nested object.
Scan each column for the odd type out. A column of numbers with three quoted strings in it is a common reason a downstream parser or chart breaks.
The column headers list every field in the response, so you see its shape without reading the API docs.
const data = [
{ id: 1, name: "John", role: "admin", tags: ["a", "b"] },
{ id: 2, name: "Jane", email: "jane@example.com" },
];
// Union of keys, in first-seen order, so ragged objects still line up
const columns = [...new Set(data.flatMap((row) => Object.keys(row)))];
const esc = (v) => String(v).replace(/&/g, "&").replace(/</g, "<");
const cell = (v) =>
v === undefined || v === null ? "" : typeof v === "object" ? JSON.stringify(v) : v;
const html =
`<table><thead><tr>${columns.map((c) => `<th>${esc(c)}</th>`).join("")}</tr></thead><tbody>` +
data.map((row) => `<tr>${columns.map((c) => `<td>${esc(cell(row[c]))}</td>`).join("")}</tr>`).join("") +
`</tbody></table>`;
console.log(html);
// In the browser: document.getElementById("out").innerHTML = html; Escape every value before putting it in innerHTML; untrusted JSON can contain HTML.
<?php
$json = '[{"id":1,"name":"John","role":"admin","tags":["a","b"]},{"id":2,"name":"Jane","email":"jane@example.com"}]';
$rows = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
// Collect every key that appears in any row
$columns = [];
foreach ($rows as $row) {
$columns = array_unique(array_merge($columns, array_keys($row)));
}
echo "<table><thead><tr>";
foreach ($columns as $c) echo "<th>" . htmlspecialchars($c) . "</th>";
echo "</tr></thead><tbody>\n";
foreach ($rows as $row) {
echo "<tr>";
foreach ($columns as $c) {
$v = $row[$c] ?? "";
if (is_array($v)) $v = json_encode($v);
echo "<td>" . htmlspecialchars((string) $v) . "</td>";
}
echo "</tr>\n";
}
echo "</tbody></table>\n"; Pass true as the second argument to json_decode() so objects become associative arrays.
import json
from html import escape
raw = '[{"id":1,"name":"John","role":"admin","tags":["a","b"]},{"id":2,"name":"Jane","email":"jane@example.com"}]'
rows = json.loads(raw)
if isinstance(rows, dict):
rows = [rows]
columns = list(dict.fromkeys(k for row in rows for k in row))
def cell(v):
if v is None:
return ""
return json.dumps(v) if isinstance(v, (dict, list)) else str(v)
html = ["<table><thead><tr>" + "".join(f"<th>{escape(c)}</th>" for c in columns) + "</tr></thead><tbody>"]
for row in rows:
html.append("<tr>" + "".join(f"<td>{escape(cell(row.get(c)))}</td>" for c in columns) + "</tr>")
html.append("</tbody></table>")
print("\n".join(html))
# With pandas: pandas.json_normalize(rows).to_html(index=False) pandas.json_normalize() also flattens nested objects into dotted columns such as address.city.
A table is a rectangle and JSON is a tree. The viewer maps one onto the other by taking one array element per row and one column per key. Nested objects become dotted columns such as user.name, while arrays are shown as JSON text in the cell. Many data tools make the same choice, so if an API is meant for tabular use, return arrays of flat objects rather than nested documents.
Columns appear in the order their keys are first seen, so the first object usually sets the leading columns. If that first record is missing a field, the column shows up further right, after whichever record introduces it. Normalize the objects before pasting if the order matters for a screenshot or a report.
This tool follows the specifications below. Read them when you need the exact normative wording, for example to settle an edge case between two implementations.
An array of objects. Each object becomes a row and the union of all keys becomes the columns. A single object is shown as a one-row table.
No, the view is static. To sort or filter, convert the JSON to CSV or XLSX and open the result in a spreadsheet.
Nested objects are flattened into dotted columns, so {"user": {"name": "Ada"}} gets a user.name column. Arrays are not flattened and show as JSON text inside their cell.
Yes, if the API allows cross-origin requests. Otherwise copy the response and paste it.
Click Copy for Email and paste into the message body. The table goes onto the clipboard as rich text with inline borders and a shaded header row, which is the only kind of styling email clients keep. The same copy pastes as a native table into Google Docs, Word, Notion and Slack, so you can send an API response to a client without opening a spreadsheet.
Copy HTML gives you code: indented <table>, <thead> and <tbody> tags with no classes or inline styles, for pasting into a page template, a CMS code editor or an HTML email template where your own CSS applies. Copy for Email gives you a rendered table with the styling baked in, for pasting into a visual editor. Use HTML when you will edit the markup, Email when you just want it to look right.
Yes. Copy Markdown produces a GitHub-flavored pipe table with a header row and a --- separator line. Pipes inside a value are escaped and line breaks become <br>, so every record stays on one row. Paste it into a README, a GitHub or GitLab issue, Notion, Obsidian or a Discord message. If you need column alignment, use the JSON to Markdown tool instead.
The tool reports the parse error with its position. For more detail, paste it into the JSON Formatter, which highlights the exact problem.
CSV to Table and PHP array to Table do the same for other sources. To export what you are looking at, use JSON to CSV or JSON to Excel.