Skip to content
simonvanlierdePublic

About

Linter for JSON, JSONC, JWCC, JSON5 and JSON Lines: syntax errors, duplicate keys and dialect rules with exact positions, JSON Schema checks, SARIF and JSON output. pre-commit hook, CLI, Rust library, GitHub Action and browser playground.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Latest commit

 

History

185 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jsonvet

CI codecov CodSpeed PyPI crates.io Marketplace License: MIT OpenSSF Scorecard OpenSSF Best Practices DOI

A linter for JSON, I-JSON, JSONC, HuJSON (JWCC), JSON5 and JSON Lines. It finds syntax errors and duplicate keys, and tells you the exact line and column:

config.json:4:5: error[duplicate-key] duplicate key $.server.port (first at 2:5)
package.json:2:14: error[syntax] missing `,`
events.jsonl:12:1: error[syntax] blank line; JSON Lines needs a value on every line

Try it in your browser: the playground checks the text you paste as you type. Nothing to install, and the text stays on your machine.

  • Six dialects, one parser. Each file is checked in the dialect its extension implies.
  • JSON Schema with positions. Each schema error points at its value's line and column. jsonvet never fetches anything from the network.
  • Fits CI. A pre-commit or prek hook, a GitHub Action, a command or a Rust library, with text, SARIF or JSON output.
  • Fast. It was check-json5, a Python hook, until 2.0 rewrote it in Rust. It is up to 1,395 times faster than check-json5 1.4.1 (figures).

Quick start

Pre-commit or prek hook

Add this to .pre-commit-config.yaml (for pre-commit or prek):

- repo: https://github.com/simonvanlierde/jsonvet
  rev: v3.2.0
  hooks:
    - id: jsonvet

It checks files by name: .json, .jsonc, .json5, .jsonl, .ndjson, .geojson, .jsonld, .hujson, .jwcc, .code-workspace, .code-snippets, .webmanifest and .babelrc.

Options go in args, such as args: [--strict, package.json] to hold package.json to strict JSON. The hook installs a prebuilt binary from PyPI, so you need no Rust.

GitHub Action

- uses: simonvanlierde/jsonvet@v3

The step fails when a file has an error, and each problem shows up as an annotation on the pull request. Before it installs jsonvet from PyPI, it checks that the wheel came from this repository's release workflow. Its inputs are in the reference.

Command line

uvx jsonvet config/*.json        # run it from PyPI without installing
uvx jsonvet .                    # check every JSON-like file under a folder
pip install jsonvet              # install it as a Python package
cargo install jsonvet-cli        # build it from source
cargo binstall jsonvet-cli       # download the binary from the GitHub release

Each GitHub release also has the binary for Linux, macOS and Windows, with checksums and an SBOM (verifying a release). A folder stands for the JSON-like files under it, skipping hidden and ignored files, and - reads standard input. jsonvet --help lists every option, and the reference has the details.

Rust library

use jsonvet::{check, Config, Dialect};

let report = check("{\"a\": 1, \"a\": 2}", &Config::new(Dialect::Json));
for problem in report.problems() {
    let at = report.location(problem.offset());
    println!("{}:{}: {}[{}] {problem}", at.line, at.column, problem.severity(), problem.rule());
}

The API is on docs.rs.

Dialects

Dialect Adds to JSON Default for
json nothing (RFC 8259) none; choose it with --dialect or --strict
i-json stricter content (RFC 7493) none
jsonc // and /* */ comments .jsonc, .code-workspace, .code-snippets
jwcc comments and trailing commas (HuJSON) .hujson, .jwcc
json5 the JSON5 1.0.0 grammar .json, .json5, .geojson, .jsonld, .webmanifest, .babelrc
jsonl one JSON value per line (JSON Lines) .jsonl, .ndjson

Any other extension is checked as JSON5. .json defaults to JSON5 because most .json files that fail as JSON are config files with comments. To check a file as strict JSON, use --dialect json:package.json.

Rules

Rule Reports Default
syntax the first syntax error; checking stops there error
duplicate-key a key repeated in one object, with both positions and its path error
invalid-number a number a double cannot hold, or an integer past ±(2⁵³ − 1) error in i-json, off elsewhere
invalid-code-point a lone surrogate or a noncharacter in a string error in i-json, off elsewhere
byte-order-mark a byte order mark at the start of the file warning in json and i-json, error in jsonl
missing-final-newline a JSON Lines file that does not end with a newline warning in jsonl

Change a rule's severity with --error, --warn or --ignore. Warnings fail a run only past --max-warnings N.

Configuration

To keep settings out of the command line, put them in jsonvet.toml, or in a [tool.jsonvet] table in pyproject.toml:

rules = { byte-order-mark = "ignore", missing-final-newline = "error" }

# Checked in order; the last matching entry wins.
dialects = [
  { files = ".vscode/*.json", dialect = "jsonc" },
  { files = "package.json", dialect = "json" },
]

# Skipped when jsonvet walks a folder; files named on the command line are still checked.
exclude = ["node_modules", "tests/fixtures/*"]

Each file takes the nearest config file in its folder or a parent, up to the repository root, so each package in a monorepo can have its own. Options on the command line win. The reference says where jsonvet looks and how globs match.

JSON Schema

--schema FILE checks each file against one JSON Schema, and reports each error at its value:

$ jsonvet --schema server.schema.json server.json5
server.json5:3:9: error[schema] "80" is not of type "integer" (at $.port)
server.json5:6:3: error[schema] Additional properties are not allowed ('debug' was unexpected) (at $.debug)

A config file can give files a schema, and so can a file's own $schema path:

schemas = [{ files = "config/server.json5", schema = "schemas/server.schema.json" }]
{ $schema: "../schemas/server.schema.json", port: 8080 }

Such a schema must stay inside the project. A $schema URL is skipped: jsonvet fetches nothing, so every $ref must point inside the schema file. Schema errors are always errors. The rules for schema paths and the limits are in the reference.

GitHub code scanning

--format sarif prints a SARIF 2.1.0 document for GitHub code scanning. The Action's sarif input writes one for you to upload:

jobs:
  jsonvet:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false
      - uses: simonvanlierde/jsonvet@v3
        with:
          sarif: jsonvet.sarif
      # Upload when jsonvet found problems too: that is when the step fails.
      - uses: github/codeql-action/upload-sarif@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
        if: success() || failure()
        with:
          sarif_file: jsonvet.sarif

Coming from check-json5

Bump rev to the newest tag. The check-json5 hook id, the check-json5 command and --strict keep working through 3.x.

jsonvet gives the same pass or fail as 1.4.1, apart from a few edge cases. One change can fail a file that passed: .jsonc, .code-workspace and .code-snippets files are now checked as JSONC, and .hujson and .jwcc files as JWCC. To keep the old verdict, add --dialect json5:GLOB.

How it is tested

The tests run on Linux, macOS and Windows, with fuzzing, mutation testing and a comparison with check-json5 1.4.1. A conformance matrix runs the same files against 18 other parsers in four languages:

dialect files share of files where each parser gives the expected verdict
JSON5 166 js-json5 100%, jsonvet-json5 100%, rs-json5 100%, py-json5 99%, py-pyjson5 99%, rs-serde-json5 94%, go-json5 91%
JSONC 9 js-jsonc-parser 100%, jsonvet-jsonc 100%, go-tidwall-jsonc 89%, js-strip-json-comments 89%, rs-jsonc-parser 78%
JWCC (HuJSON) 10 jsonvet-jwcc 100%, go-hujson 90%
JSON Lines 11 jsonvet-jsonl 100%, py-jsonlines 82%, go-json-stream 64%, rs-serde-json-stream 64%
Strict JSON (baseline) 129 js-json-parse 100%, jsonvet-json 100%, rs-serde-json 98%, py-json 96%, py-jiter 95%

Run on 2026-10-28. The full grid, and where each expected verdict comes from: docs/conformance.md.

More

Found a file jsonvet gets wrong? Please open an issue with the file and the command. If it crashes or hangs jsonvet, report it privately as SECURITY.md says.

To cite jsonvet, use the DOI 10.5281/zenodo.23461825 or GitHub's "Cite this repository" button.

Credits and license

I (Simon van Lierde) started this as a fork of check-json5 by Ben Mares, which is based on the pre-commit-hooks repository by Anthony Sottile and contributors. It is MIT-licensed, like both of them.

About

Linter for JSON, JSONC, JWCC, JSON5 and JSON Lines: syntax errors, duplicate keys and dialect rules with exact positions, JSON Schema checks, SARIF and JSON output. pre-commit hook, CLI, Rust library, GitHub Action and browser playground.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages