JSON to Markdown Table Converter

Put an API response into an issue or a design doc where people will actually read it. Nested fields become dotted columns and the numeric columns line up.

To convert JSON to a Markdown table, paste your array of objects above. The keys become the header row, each object becomes a table row, nested objects flatten into dotted columns, pipes inside values are escaped so they do not split cells, and numeric columns are right-aligned so the figures line up.

Want to cut it down to the useful fields? Open the app

Nobody reads a JSON blob in a pull request

A code review that pastes forty lines of pretty-printed JSON gets a thumbs up and no scrutiny. The same data as a five-row table gets read, because the eye can compare rows and cannot compare indented braces.

The same applies to incident notes, where the useful artefact is the six records that were wrong, and to design docs, where the point is to show what a response looks like rather than to specify it exhaustively.

So the conversion is not really about format. It is about whether the person you are showing it to will look.

Worked example

The order response again:

[
  {
    "order_id": "ORD-00001",
    "placed_at": "2024-05-13T01:17:00Z",
    "status": "shipped",
    "customer": { "id": 4188, "name": "Katherine Johnson", "city": "Osaka" },
    "items": 8,
    "total": 2151.83,
    "gift": false
  },
  {
    "order_id": "ORD-00002",
    "placed_at": "2024-08-10T11:39:00Z",
    "status": "delivered",
    "customer": { "id": 3737, "name": "Ada Lovelace", "city": "Berlin" },
    "items": 1,
    "total": 1783.46,
    "gift": true
  }
]

And the Markdown:

| order_id | placed_at | status | customer.id | customer.name | customer.city | items | total | gift |
| --- | --- | --- | ---: | --- | --- | ---: | ---: | --- |
| ORD-00001 | 2024-05-13T01:17:00Z | shipped | 4188 | Katherine Johnson | Osaka | 8 | 2151.83 | false |
| ORD-00002 | 2024-08-10T11:39:00Z | delivered | 3737 | Ada Lovelace | Berlin | 1 | 1783.46 | true |

The three numeric columns are right-aligned and nothing else is. order_id looks like an identifier and is one, so it stays left. That is the whole of the auto alignment rule, and it is the difference between a table you can scan for the big number and one where the digits wander.

What flattening does to a nested object

A nested object becomes dotted columns. "customer": { "name": "Ada" } becomes a column called customer.name. That is the one convention every tool in this space agrees on, and it is reversible enough that a person reading the header knows exactly where the value came from.

Nesting deeper than four levels is kept as JSON text in a single cell instead of exploding into columns nobody will use. Four is deep enough for every API response worth tabulating and shallow enough that the header row stays readable.

An array of objects is also kept as JSON text in one cell, rather than being spread across extra rows. Spreading it would silently change the row count, so a file of 200 orders would come back as 4,000 rows and the totals would all be wrong. If that is the shape you want, it is a join rather than a flatten, and the full editor does it explicitly.

Every one of those decisions is reported under the result, with a count. Nothing about the structure changes quietly.

The escaping, and the width problem

A pipe inside a value ends the cell, so every one is escaped as \|, with backslashes escaped first so a value ending in one cannot consume the escape after it. JSON strings hold arbitrary text and pipes turn up in log lines and free-text notes constantly.

A newline inside a JSON string becomes <br>, which GitHub renders as a line break. Markdown has no row continuation, so the alternative is deleting the break and silently changing the value.

The real constraint with JSON as a source is width. A response with twenty fields, three of them nested, produces a table nobody can read on a laptop. Cutting the columns first is almost always the right move, and the escape-hatch link above the fold opens the same data in an editor where you can pick five fields and come back.

Types, and why booleans are not ticks

A JSON boolean comes through as the text true or false. That is deliberately literal: turning them into ticks and crosses reads better and stops the table being a faithful record of the response, which is the whole reason it is in the review.

A null comes through as an empty cell rather than the word null, because an empty cell reads as absence and the word reads as a value. If the distinction matters in your document, say so in a sentence under the table, where a reader will actually see it.

Frequently Asked Questions

How are nested objects handled?

They flatten into dotted columns, so a nested customer becomes customer.id, customer.name and customer.city. Nesting past four levels is kept as JSON text in one cell rather than producing a header row that wraps three times.

What happens to an array of objects inside a record?

It stays as JSON text in one cell. Spreading it into extra rows would change the row count, and a table in a review that quietly has five times as many rows as the response is worse than a cell with some JSON in it.

Are pipes in my strings escaped?

Every one, with backslashes escaped first so a value ending in a backslash cannot eat the escape that follows. Log lines and free-text notes are full of pipes, and one unescaped pipe breaks the whole table.

Why is order_id left-aligned when it has digits in it?

Because it is not a number, it is an identifier with a prefix. Auto alignment follows the type decided from all the values in the column, not the shape of the characters, so identifiers stay left and quantities go right.

Do booleans become ticks?

No, they stay as the words true and false. Ticks read better and stop the table being a faithful record of the response, which is usually why it is in the document in the first place.

My response has twenty fields. Is that going to work?

It will convert, and it will be too wide to read. Cutting the columns first is the right move for anything past about eight fields; the escape-hatch link opens the same data in an editor where you can pick the ones that matter.

Put the response where people will read it

Flattened, escaped, aligned. Copy and paste into the issue.

Back to the converter