はじめに
JSONを扱っていると、たまに「こんなの来るなよ」というデータに遭遇することがあります。
外部APIから返ってきたJSONが微妙に壊れている、LLMの出力がシングルクォートになっている、手動編集したJSONファイルのカンマが抜けている...。エンジニアあるあるですよね。
jsonrepairは、そういった不正なJSONを自動で修復してくれるライブラリです。GitHubで2,000スター以上、月間330万ダウンロードという実績。個人的には「もっと早く知りたかった」系のツールですね。
jsonrepairとは
jsonrepairは、Jos de Jong氏が開発したJavaScript/TypeScript向けのJSON修復ライブラリです。
名前の通り「JSONを修理する」ためのツールで、構文エラーのあるJSONを自動的に有効なJSONに変換してくれます。ブラウザでもNode.jsでも動作し、ストリーミング処理にも対応しています。
バンドルサイズは約2.45kB(minified + gzipped)と超軽量。これ、意外と重要なポイントなんですよ。JSONパーサー系のライブラリって重くなりがちなので。
特徴・メリット
1. 修復できる問題が幅広い
jsonrepairが対応している問題は、実際に現場で遭遇するものばかり。
- キーの引用符が欠けている(
{name: "John"}→{"name": "John"}) - シングルクォートが使われている(
{'name': 'John'}→{"name": "John"}) - 末尾のカンマがある(
{"a": 1,}→{"a": 1}) - カンマが抜けている(
{"a": 1 "b": 2}→{"a": 1, "b": 2}) - 閉じ括弧が足りない(
{"a": 1→{"a": 1}) - コメントが含まれている(
/* comment */を削除) - Pythonの定数が混在(
None→null、True→true) - 特殊な引用符(
"..."→"...")
正直なところ、これだけカバーしてくれれば大抵の問題は解決できます。
2. LLM出力との相性が抜群
最近のLLM(ChatGPT、Claude等)にJSONを出力させると、たまに不正なJSONが返ってくることがあるんですよ。シングルクォートになっていたり、末尾カンマがあったり。
jsonrepairを噛ませておけば、そういった不正な出力も問題なく処理できます。LLMを業務で使う人には特におすすめ。
3. ストリーミング対応
大きなJSONファイルを扱う場合、メモリに全部載せるのは現実的じゃない。jsonrepairはストリーミングAPIを提供しているので、512MBを超えるような巨大ファイルも処理できます。
4. CLIツールも付属
npmでグローバルインストールすれば、コマンドラインからも使えます。ファイルを直接修復したいときに便利。
インストール方法
npmでインストール
npm install jsonrepair
これだけ。依存関係もないので、シンプルにプロジェクトに追加できます。
CLIとして使う場合
npm install -g jsonrepair
グローバルインストールすれば、どこからでもjsonrepairコマンドが使えるようになります。
基本的な使い方
ESモジュールでの使用
import { jsonrepair } from 'jsonrepair'
// シングルクォートを修復
const result1 = jsonrepair("{'name': 'John'}")
console.log(result1) // {"name": "John"}
// キーの引用符を補完
const result2 = jsonrepair("{name: 'John', age: 30}")
console.log(result2) // {"name": "John", "age": 30}
// 末尾カンマを削除
const result3 = jsonrepair('{"items": [1, 2, 3,]}')
console.log(result3) // {"items": [1, 2, 3]}
// 閉じ括弧を補完
const result4 = jsonrepair('{"a": {"b": 1}')
console.log(result4) // {"a": {"b": 1}}
使い方は単純明快。文字列を渡すだけで、有効なJSONが返ってきます。
エラーハンドリング
修復不可能なケースもあるので、try-catchで囲むのが基本。
import { jsonrepair, JSONRepairError } from 'jsonrepair'
try {
const repaired = jsonrepair(brokenJson)
const data = JSON.parse(repaired)
// 処理を続行
} catch (error) {
if (error instanceof JSONRepairError) {
console.error('JSONの修復に失敗しました:', error.message)
} else {
throw error
}
}
ストリーミング処理
大きなファイルを扱う場合は、ストリーミングAPIを使います。
import { jsonrepairTransform } from 'jsonrepair/stream'
import { createReadStream, createWriteStream } from 'node:fs'
import { pipeline } from 'node:stream'
const inputStream = createReadStream('./broken.json')
const outputStream = createWriteStream('./repaired.json')
pipeline(
inputStream,
jsonrepairTransform(),
outputStream,
(error) => {
if (error) {
console.error('処理中にエラーが発生しました:', error)
} else {
console.log('修復が完了しました')
}
}
)
CLIでの使用
# 標準入力から読んで標準出力に出力
echo "{'name': 'John'}" | jsonrepair
# 出力: {"name": "John"}
# ファイルを修復して別ファイルに出力
jsonrepair broken.json --output repaired.json
# ファイルを直接上書き
jsonrepair broken.json --overwrite
時短になるので、ちょっとした作業にはCLIが便利ですね。
実践的なユースケース
LLMの出力を安全にパースする
import { jsonrepair } from 'jsonrepair'
async function parseLLMResponse(response: string): Promise<unknown> {
// LLMの出力をまず修復してからパース
const repaired = jsonrepair(response)
return JSON.parse(repaired)
}
// 使用例
const llmOutput = `{
name: 'Claude',
capabilities: ['text', 'code',],
active: True
}`
const data = await parseLLMResponse(llmOutput)
console.log(data)
// { name: 'Claude', capabilities: ['text', 'code'], active: true }
個人的には、LLM連携のプロジェクトでは必須だと思います。
設定ファイルのバリデーション
手動編集された設定ファイルにありがちなミスを自動修復。
import { jsonrepair } from 'jsonrepair'
import * as fs from 'fs'
function loadConfig(path: string): Record<string, unknown> {
const raw = fs.readFileSync(path, 'utf-8')
try {
// まず普通にパースを試みる
return JSON.parse(raw)
} catch {
// 失敗したら修復してからパース
console.warn('設定ファイルに問題があったため修復しました')
const repaired = jsonrepair(raw)
return JSON.parse(repaired)
}
}
const config = loadConfig('./config.json')
APIレスポンスの前処理
外部APIが微妙に壊れたJSONを返してくる場合の対策。
import { jsonrepair } from 'jsonrepair'
async function fetchData(url: string): Promise<unknown> {
const response = await fetch(url)
const text = await response.text()
// 念のため修復を噛ませる
const repaired = jsonrepair(text)
return JSON.parse(repaired)
}
JSONLファイルの修復
ログファイルなど、1行1JSONの形式でも使えます。
import { jsonrepair } from 'jsonrepair'
import * as readline from 'readline'
import * as fs from 'fs'
async function processJsonl(inputPath: string): Promise<void> {
const fileStream = fs.createReadStream(inputPath)
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
})
for await (const line of rl) {
if (line.trim()) {
const repaired = jsonrepair(line)
const data = JSON.parse(repaired)
// データを処理
console.log(data)
}
}
}
パフォーマンスについて
jsonrepairは軽量で高速です。ただし、常に修復処理を通すのはオーバーヘッドになる可能性があります。
// 推奨パターン: まず普通にパースを試みて、失敗したら修復
function safeJsonParse(text: string): unknown {
try {
return JSON.parse(text)
} catch {
const repaired = jsonrepair(text)
return JSON.parse(repaired)
}
}
正常なJSONが多い場合は、このパターンがコスパ的に良いですね。
まとめ
jsonrepairを使って感じたこと:
- 導入の手軽さ: インストールして関数を呼ぶだけ
- カバー範囲: 実務で遭遇する問題はほぼ対応
- サイズ: 2.45kBで軽量
- 信頼性: 月間330万ダウンロードの実績
正直なところ、JSONを外部から受け取るシステムでは、jsonrepairを入れておいて損はないと思います。特にLLMを活用したアプリケーションを作っている人には一択ですね。
「JSONのパースで落ちた」というインシデントを未然に防げるので、保険として入れておくのがおすすめです。QOL上がりますよ。