Documenting REST API endpoints, database exports, and configuration schemas often requires translating deeply nested JSON payloads into readable tabular formats. While JSON is the universal standard for machine-to-machine data interchange, it is notoriously difficult for humans to scan quickly in pull request descriptions, Notion pages, and GitHub README.md files.

Converting JSON arrays into GitHub-Flavored Markdown (GFM) tables manually is an error-prone task that involves tedious pipe formatting and character escaping. This guide covers how to automate JSON to Markdown conversion using standard CLI tools, discusses the structural challenges of formatting nested data, and introduces a local browser approach for processing data.

The Developer Workflow: Converting JSON to Markdown via CLI

For developers comfortable with the command line, jq (a lightweight JSON processor) provides a powerful way to script Markdown table generation.

If you have a perfectly uniform array of objects where every key matches a column, you can write a jq filter to extract the keys for the header, map a delimiter row, and iterate through the values.

The jq Pipeline Strategy

Assume you have a standard JSON payload containing API endpoint statuses saved as data.json:

[
  { "id": 1, "endpoint": "/users", "status": "200 OK" },
  { "id": 2, "endpoint": "/billing", "status": "403 Forbidden" }
]

You can execute the following bash command locally to format this array into a GFM table:

jq -r '
  (.[0] | keys_unsorted | join(" | ")), 
  (.[0] | keys_unsorted | map("---") | join(" | ")), 
  (.[] | map(tostring) | join(" | "))
' data.json | sed 's/^/| /; s/$/ |/'

Execution Output:

| id | endpoint | status |
| --- | --- | --- |
| 1 | /users | 200 OK |
| 2 | /billing | 403 Forbidden |

While this script is elegant for simple flat arrays, it breaks under real-world conditions. If your API payload features heterogeneous keys (objects with missing or optional properties), jq will misalign the columns because the header is taken only from the first object while values are emitted positionally. Furthermore, jq does not automatically escape native Markdown table characters like internal pipes (|) or line breaks (\n), which will silently corrupt your document layout.

A robust jq filter

To handle missing keys, collect every key across the array using keys_unsorted (which keeps insertion order as per the jq manual). This code also escapes pipes and replaces newlines with <br> tags.

Save this as tomd.jq and run it with jq -r -f tomd.jq data.json:

def cell: if . == null then "" elif type == "string" then . else tojson end
  | gsub("\\|"; "\\|") | gsub("\n"; "<br>");
(reduce (.[] | keys_unsorted[]) as $k ([]; if index([$k]) then . else . + [$k] end)) as $cols
| ($cols | join(" | ")),
  ($cols | map("---") | join(" | ")),
  (.[] as $row | $cols | map($row[.] | cell) | join(" | "))
| "| " + . + " |"

Tested input:

[
  { "id": 1, "endpoint": "/users", "status": "200 OK" },
  { "id": 2, "status": "403 Forbidden", "note": "a|b\nline2", "roles": ["admin", "editor"] }
]

Tested output:

| id | endpoint | status | note | roles |
| --- | --- | --- | --- | --- |
| 1 | /users | 200 OK |  |  |
| 2 |  | 403 Forbidden | a\|b<br>line2 | ["admin","editor"] |

Using the JSON to Markdown converter

When CLI scripts are too complex or you need a quick table, you can use Utiliome’s JSON to Markdown Converter. It uses plain JSON.parse to convert arrays and objects into Markdown tables. The tool runs in your browser, so the file is not uploaded.

  • Smart Key Normalization: The converter collects all keys across objects and leaves empty cells for missing ones.
  • Data Formatting: It automatically escapes | and turns newlines into <br> in plain values.
  • Handling Primitives and Objects: A single object becomes a Key/Value table, while an array of primitives becomes a single Value column.
  • Null Values: A null shows as the text null.
  • File Handling: Supports .json upload and .md download.

Handling Edge Cases: Nested Objects and Arrays

Markdown tables are inherently two-dimensional. They do not natively support multidimensional arrays or nested sub-tables. The converter writes nested objects and arrays as compact JSON directly in the cell, as it does not flatten.

If you want separate columns for nested data, you must manually flatten it or write a custom jq filter.

Manual Dot-Notation Property Flattening

When an object property contains a nested JSON object, you can manually flatten the nested paths into explicit header names using dot-notation:

user.nameuser.location.city
AlexSeattle

Manual Inline String Serialization

If a property contains an embedded array, representing it as a comma-delimited inline string prevents Markdown syntax corruption.

UsernameAssigned Roles
dev_useradmin, editor

JSON Documentation Pre-Flight Checklist

Before committing a generated table to your docs, check:

  • 1. Verify Pipe Escaping: Ensure that any string values containing vertical pipes (|) within the JSON were successfully escaped as \| to prevent breaking the table cell boundaries. For more details, see escaping pipes in Markdown tables.
  • 2. Validate GFM Alignment: Confirm that your numerical columns (e.g., byte sizes, latencies) use right alignment (---:) in the delimiter row for optimal readability, while string descriptions remain left-aligned (:---) (see GitHub Docs: tables).