JSON5 for Python

Parse and work with JSON5 files in your Python applications.

TL;DR: Install with pip install json5, then use json5.load() and json5.loads() just like the standard json module.

Installation

The most popular JSON5 library for Python is json5, available on PyPI.

pip

pip install json5

pipenv

pipenv install json5

Poetry

poetry add json5

uv

uv add json5

conda

conda install -c conda-forge json5

1M+

Monthly downloads

Python 3.8+

Declared floor; 3.11+ still gets security fixes

Apache-2.0

License

Basic Usage

The json5 library provides an API nearly identical to Python's built-in json module.

Parsing JSON5 Strings

import json5 # Parse JSON5 string with comments and trailing commas config_string = """ { // Application settings name: 'my-app', version: '1.0.0', features: [ 'comments', 'trailing commas', ], } """ config = json5.loads(config_string) print(config['name']) # 'my-app' print(config['features']) # ['comments', 'trailing commas']

Converting Python to JSON5

import json5 data = { 'name': 'my-app', 'debug': True, 'ports': [3000, 3001], } # Convert to JSON5 string json5_string = json5.dumps(data, indent=2) print(json5_string) # { # name: "my-app", # debug: true, # ports: [ # 3000, # 3001, # ], # } # Keys are unquoted and strings are double-quoted by default. # For single quotes, pass # quote_style=json5.QuoteStyle.ALWAYS_SINGLE

Drop-in Replacement for json

# Replace this: import json # With this: import json5 as json # Your existing code works with JSON5 features! data = json.loads('{ name: "app", /* comment */ }')

API Reference

json5.loads(s, *, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, strict=True, object_pairs_hook=None, allow_duplicate_keys=True, consume_trailing=True, start=None)

Deserialize a JSON5 string to a Python object. The first group of parameters matches the standard json.loads(); allow_duplicate_keys, consume_trailing and start are JSON5-specific. An invalid document raises ValueError, not json.JSONDecodeError.

import json5 from decimal import Decimal # With custom float parser data = json5.loads( '{ price: 19.99 }', parse_float=Decimal ) print(type(data['price'])) # <class 'decimal.Decimal'>

json5.load(fp, *, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, strict=True, object_pairs_hook=None, allow_duplicate_keys=True, consume_trailing=True, start=None)

Deserialize a JSON5 file-like object to a Python object. Takes the same keyword arguments as loads(), and the file may be opened in text or binary mode.

import json5 with open('config.json5', 'r') as f: config = json5.load(f) print(config)

json5.dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, quote_keys=False, trailing_commas=True, allow_duplicate_keys=True, quote_style=QuoteStyle.ALWAYS_DOUBLE)

Serialize a Python object to a JSON5 formatted string. The last four parameters control the JSON5 output shape: keys are left unquoted, multi-line output gets a trailing comma, and string values are double-quoted unless you change quote_style.

import json5 data = {'name': 'test', 'value': 42} # Compact print(json5.dumps(data)) # {name: "test", value: 42} # Pretty-printed print(json5.dumps(data, indent=2)) # { # name: "test", # value: 42, # } # With sorted keys print(json5.dumps(data, sort_keys=True))

json5.dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)

Serialize a Python object to a JSON5 file.

import json5 data = {'name': 'app', 'version': '1.0'} with open('output.json5', 'w') as f: json5.dump(data, f, indent=2)

Reading JSON5 Files

Basic File Reading

import json5 def load_config(path: str) -> dict: """Load a JSON5 configuration file.""" with open(path, 'r', encoding='utf-8') as f: return json5.load(f) config = load_config('config.json5') print(config)

With Error Handling

import json5 from pathlib import Path def load_config(path: str, defaults: dict = None) -> dict: """Load JSON5 config with defaults and error handling.""" config_path = Path(path) defaults = defaults or {} if not config_path.exists(): print(f"Config not found: {path}, using defaults") return defaults try: with open(config_path, 'r', encoding='utf-8') as f: config = json5.load(f) return {**defaults, **config} except ValueError as e: # json5 raises ValueError on a parse error print(f"Error parsing {path}: {e}") raise config = load_config('config.json5', { 'debug': False, 'port': 3000, })

With Pydantic Validation

import json5 from pydantic import BaseModel from typing import List, Optional class DatabaseConfig(BaseModel): host: str port: int database: str ssl: bool = False class AppConfig(BaseModel): name: str version: str debug: bool = False database: DatabaseConfig def load_validated_config(path: str) -> AppConfig: """Load and validate JSON5 config with Pydantic.""" with open(path, 'r') as f: data = json5.load(f) return AppConfig(**data) config = load_validated_config('config.json5') print(config.database.host) # Fully typed!

Command Line Usage

the json5 package includes a command-line tool that parses and pretty-prints JSON5 files. Note that it prints JSON5 by default - pass --as-json when you want standard JSON out.

Convert JSON5 to JSON

# Convert JSON5 to JSON (quotes keys, drops trailing commas) python -m json5 --as-json config.json5 > config.json # Reformat as JSON5 instead (the default) python -m json5 config.json5 # Or from stdin cat config.json5 | python -m json5 --as-json

Validate JSON5

There is no --check flag. The tool exits non-zero on a parse error, so discarding its output is the validation check:

# Check if file is valid JSON5 python -m json5 config.json5 > /dev/null && echo "Valid JSON5"

Other Flags

-c STR parse an inline string instead of a file --indent N spaces per level (default 4) --quote-keys quote every object key --no-trailing-commas omit the trailing comma in multi-line output --quote-style STYLE always_double | always_single | prefer_double | prefer_single --strict / --no-strict reject or allow control characters in strings -V print the library version

Alternative Libraries

While json5 is the most popular choice, here are other Python JSON5 libraries:

pyjson5

A compiled Cython parser, orders of magnitude faster at reading. Its encoder emits JSON-shaped output - quoted keys, no trailing commas - so it reads JSON5 but does not write it. Recent releases add free-threaded (no-GIL) builds.

pip install pyjson5 import pyjson5 data = pyjson5.loads('{ key: "value" }') # Parse errors raise its own exception class, not ValueError - # the two libraries are not error-compatible

commentjson

Unmaintained - last release 2020, and it pins a version of lark-parser abandoned in 2021. It handles // and # line comments and trailing commas only: block comments, unquoted keys and single-quoted strings all fail. Not a JSON5 parser.

pip install commentjson import commentjson data = commentjson.loads('{ "key": "value" // comment\n }') # This raises ValueError - no block-comment support: # commentjson.loads('{ "key": "value" /* comment */ }')

Recommendation: Use json5 for config-sized files, for portability - it is pure Python with no dependencies - and whenever you need to write JSON5. Switch to pyjson5 when read throughput matters: json5's own README warns it "is SLOW. It can be 1000-6000x slower than the C-optimized JSON module, and is 200x slower (or more) than the pure Python JSON module." For a config file loaded once at startup that is irrelevant; for parsing in a loop it is not.

Common Patterns

Environment-Specific Config

import json5 import os from pathlib import Path def load_env_config() -> dict: """Load config based on environment.""" env = os.getenv('APP_ENV', 'development') config_dir = Path('config') # Load base config with open(config_dir / 'base.json5') as f: config = json5.load(f) # Override with environment-specific config env_config_path = config_dir / f'{env}.json5' if env_config_path.exists(): with open(env_config_path) as f: env_config = json5.load(f) config.update(env_config) return config

Type Hints with TypedDict

import json5 from typing import TypedDict, List class ServerConfig(TypedDict): host: str port: int workers: int class AppConfig(TypedDict): name: str version: str server: ServerConfig features: List[str] def load_config(path: str) -> AppConfig: with open(path) as f: return json5.load(f) # type: ignore config: AppConfig = load_config('config.json5') print(config['server']['port']) # Type checker knows this is int

Ready to Use JSON5 in Python?

Explore more examples or check out other language guides.