A production-ready PHP library for parsing JSONC (JSON with Comments) format with drop-in compatibility for json_decode().
- Single-line comments (
//) and multi-line comments (/* */) - Trailing commas in objects and arrays
- Drop-in replacement for
json_decode()— same signature, same error behavior, verified by a differential test suite - Edge case handling: Preserves strings with comment syntax, escaped characters, Unicode
- Strict by default: malformed input (raw control bytes, unclosed strings or comments) is rejected like native
json_decode()does — never silently sanitized - Fast: plain JSON takes a native
json_decode()fast path; JSONC goes through a single-pass scanner - Zero dependencies: Uses native PHP JSON extension
- Well tested: 150+ tests with 100% code coverage, PHPStan at max level
composer require otar/jsonc// Use global function wrapper
$data = jsonc_decode($jsonc, true);
// Check for errors using native PHP functions
if (json_last_error() !== JSON_ERROR_NONE) {
echo json_last_error_msg();
}use Otar\JSONC;
$jsonc = '{
// This is a comment
"name": "John Doe",
"age": 30,
"hobbies": [
"reading",
"coding", // Inline comment
],
}';
$data = JSONC::decode($jsonc, true);
// ['name' => 'John Doe', 'age' => 30, 'hobbies' => ['reading', 'coding']]use Otar\JSONC;
$jsonc = '{/* comment */"key": "value",}';
$json = JSONC::parse($jsonc);
// '{ "key": "value"}' — comments become a space, trailing commas are dropped
// Now you can use with standard json_decode
$data = json_decode($json, true);Decodes a JSONC string. Drop-in replacement for json_decode().
public static function decode(
string $jsonc,
?bool $associative = null,
int $depth = 512,
int $flags = 0
): mixedParses JSONC string and returns cleaned JSON string (without comments and trailing commas).
public static function parse(string $jsonc): stringThrows Otar\JsoncSyntaxException when the input ends inside an unclosed string literal or block comment — it never returns invalid JSON.
Global function alias for JSONC::decode().
decode() follows json_decode() exactly: invalid input returns null and sets the native error state.
$invalidJsonc = '{invalid json}';
$result = JSONC::decode($invalidJsonc);
if ($result === null && json_last_error() !== JSON_ERROR_NONE) {
echo 'Parse error: ' . json_last_error_msg();
}With JSON_THROW_ON_ERROR it throws instead. Unclosed JSONC constructs surface as Otar\JsoncSyntaxException — a JsonException subclass that reports where the construct opened:
use Otar\JsoncSyntaxException;
try {
JSONC::decode('{"a": 1, /* unterminated', flags: JSON_THROW_ON_ERROR);
} catch (JsoncSyntaxException $e) {
echo $e->getMessage(); // Unclosed block comment starting at offset 9
$e->getOffset(); // 9
} catch (JsonException $e) {
// any other JSON syntax error
}JSONC::parse() always throws JsoncSyntaxException for unclosed constructs, with or without flags.
- A leading UTF-8 BOM is tolerated and stripped (native
json_decode()rejects it) — JSONC config files frequently carry one. - Comments and trailing commas are accepted.
- Everything else matches native behavior, including
json_last_error()codes. json_last_error()is only meaningful afterdecode();parse()may probe the input internally, which writes the global JSON error state.
- PHP 8.1 or higher
- JSON extension (enabled by default in PHP)
# Run tests
composer test
# Run tests with coverage
composer test-coverage
# Static analysis (PHPStan, level max)
composer phpstan
# Micro-benchmark
composer benchMIT License - see LICENSE file for details.
Contributions are welcome! Please ensure all tests pass (composer test) and maintain code coverage.
Inspired by existing JSONC parsers and the need for a robust, production-ready PHP implementation with proper edge case handling.
- microsoft/node-jsonc-parser - Official JSONC parser for Node.js
- VS Code - Uses JSONC for configuration files