JSON All-in-OneJSON All-in-One
Site navigation
English
Practical guide

How to use jq with JSONC

Standard command-line jq expects strict JSON and rejects comments and trailing commas. JSON All-in-One can run jq against JSONC with comments: its ordinary jq route strips comments but currently rejects trailing commas. A direct projection over a root array can take a separate item-by-item fast path that accepts them.

Open the file in the Chrome or Edge extension to try the browser examples. For a terminal pipeline, convert JSONC to strict JSON before jq. The examples distinguish the ordinary jq route from the specialized array route rather than calling every JSONC file or jq expression equivalent.

A config file that strict jq rejects

Imagine a service config with a production endpoint, two routes, and a comment explaining why one route is disabled. The URL contains // as data; the file also has a line comment and a block comment. Removing comments with a regular expression would be an easy way to damage that URL.

Save this as config.jsonc. It is one root object, not an array. The query in the next section therefore starts at .routes, not .[].

config.jsoncjsonc
{
  // Deployed API endpoint; the double slash in the path is intentional.
  "endpoint": "https://api.example.test/v1//status",
  "routes": [
    { "name": "checkout", "enabled": true, "retries": 3 },
    /* Keep the legacy route for rollback, but do not send traffic to it. */
    { "name": "legacy", "enabled": false, "retries": 0 }
  ]
}

Run jq on JSONC in the browser

Open config.jsonc in JSON All-in-One. You can use the in-workbench file picker; opening a file:// URL directly requires the browser's file-access permission for the extension. Switch to JQ, paste the expression below, then press Ctrl+Enter on Windows or Linux or Cmd+Enter on macOS to open the transformed result. Keep trailing commas out of this root-object file: this filter uses ordinary jq, not the array fast path.

The filter keeps enabled routes and returns a compact array of name/retry pairs. Comments are not query values, and the source file stays available separately. Pressing Enter alone is not the same as running the transformation.

jq filterjq
.routes | map(select(.enabled) | {name, retries})
Expected resultjson
[
  {
    "name": "checkout",
    "retries": 3
  }
]

A root array has a narrower fast path

If the file itself is an array, JSON All-in-One recognizes a direct projection such as .[] | .name when you run it against the original source. It reads one top-level item at a time and writes the projected array incrementally to browser storage. The JSONC parser can handle the trailing commas in this array example; the ordinary jq engine used above cannot.

This is a deliberately small filter shape, not general streaming jq. .[] | select(.enabled == true) has a different per-item path during initial loading, but its matches are kept for display. A filter such as map(select(.enabled)) or a query over a root object goes through ordinary jq instead. If the source has trailing commas, do not assume those expressions will work merely because the direct projection did.

routes.jsonc: root array with trailing commasjsonc
[
  // Current production routes.
  { "name": "checkout", "enabled": true, },
  { "name": "legacy", "enabled": false, },
]
Direct projectionjq
.[] | .name
Expected projected namesjson
[
  "checkout",
  "legacy"
]

Terminal option: convert JSONC, then pipe to jq

If you already use Node.js, strip-json-comments-cli is a short route for this file. Its CLI strips both comments and trailing commas, then writes strict JSON to standard output. jq receives that clean JSON, so the filter is unchanged even if a future edit adds trailing commas to the configuration.

npx may download the package on first use. For a repeatable project workflow, add a reviewed version to your dependencies instead of silently fetching a moving version every time. Do not use a hand-written comment-stripping regex: // inside endpoint is part of the string.

Node.js CLI and jqsh
npx --yes strip-json-comments-cli config.jsonc | jq '.routes | map(select(.enabled) | {name, retries})'

Terminal option: parse with Python first

The json-with-comments package offers a Python alternative, imported as jsonc. Version 1.3.0 handles // and /* ... */ comments and trailing commas, including the URL slashes in config.jsonc above. It loads the file as a Python value; json.dump writes JSON that jq can read. The allow_nan=False option rejects non-finite numbers that strict JSON cannot represent.

Install the pinned version in a virtual environment first. This is a whole-document parse for configuration-sized files; memory use grows with the document. Parsing and writing can change number formatting, Python floats can round decimal values, and jq has its own numeric limits. Keep precision-sensitive identifiers as strings and check numeric values before using the converted output.

Install the parsersh
python3 -m pip install json-with-comments==1.3.0
Parse JSONC, then run jqsh
python3 -c "import json, jsonc, sys; json.dump(jsonc.load(open('config.jsonc', encoding='utf-8')), sys.stdout, allow_nan=False)" | jq '.routes | map(select(.enabled) | {name, retries})'

Why gojq --yaml-input is not a JSONC switch

gojq is a useful jq implementation with a --yaml-input option. That option selects a YAML decoder, not a JSONC decoder. YAML comments start with #, while this file uses // and /* ... */. Its permissive flow syntax may accept a trailing comma, but that does not make it a reliable JSONC parser.

You may see the command below suggested for a .jsonc file. Do not use it as a general answer to jq plus JSONC: on real JSONC it can fail or interpret the text differently. Use it for YAML input, or convert JSONC with a JSONC-aware tool first and then run jq or gojq normally.

Not a general JSONC conversion commandsh
gojq --yaml-input '.' config.jsonc

Reading in chunks is not the same as querying in a stream

gojq opens files through a reader, but its ordinary JSON decoder produces one complete value for each top-level document before a filter such as . runs. With --yaml-input, the YAML decoder likewise produces a value for each YAML document. That is different from gojq's --stream mode, which emits path/value events for JSON and requires a stream-aware filter; --stream is not a JSONC parser.

JSON All-in-One can read and prepare a large local source in chunks without expanding the whole tree for inspection. For a root array, a direct projection such as .[] | .name has a specialized fast path on the original source: it visits one top-level item at a time and writes the result incrementally to browser storage. That path can avoid holding the entire input in the jq worker. A simple equality filter such as .[] | select(.enabled == true) can also be checked item by item during initial loading, but matching records are retained for display, so memory still grows with the matches.

These are specific filter shapes, not a promise for arbitrary jq. The root-object .routes | map(...) example above does not take the array fast path: ordinary jq execution buffers the input in the worker. A single very large array item, a broad result, or an expensive expression can still cost substantial memory. We have not measured a universal memory advantage over gojq. For large datasets, see opening large JSON files before choosing a broad recursive query.

Why not use the C preprocessor?

The command below sometimes appears as a quick way to remove comments. It is not a JSONC conversion recipe. cpp preprocesses C-like source: it can process directives and macros, and -P only suppresses line markers. It also does not fix JSONC trailing commas, so strict jq would still reject the root-array example above.

Keep it out of scripts that handle configuration you care about. A JSONC-aware parser or converter is both clearer about the accepted syntax and easier to test against a fixture with strings, comments, and trailing commas.

Avoid as a general JSONC pipelinesh
cpp -P -E config.jsonc | jq '.'