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 json5pipenv
pipenv install json5Poetry
poetry add json5uv
uv add json5conda
conda install -c conda-forge json51M+
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_SINGLEDrop-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-jsonValidate 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 versionAlternative 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-compatiblecommentjson
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 configType 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 intReady to Use JSON5 in Python?
Explore more examples or check out other language guides.