JavaScript Tips | 基本・共通ユーティリティ:安全処理 – 安全な JSON parse

JavaScript JavaScript
スポンサーリンク

JSON.parse が「危険」になりやすい理由

まず前提として、JSON.parse は「JSON 形式の文字列を、JavaScript の値(オブジェクトや配列など)に変換する関数」です。

const json = '{"id":1,"name":"山田"}';
const obj = JSON.parse(json);

console.log(obj.id);   // 1
console.log(obj.name); // "山田"
JavaScript

ここまではとても便利ですが、問題は「壊れた JSON が来たとき」です。
JSON.parse は、パースに失敗すると 例外を投げて処理を止めてしまう という性質があります。

const badJson = '{id:1,"name":"山田"}'; // JSON として不正(id にダブルクォートがない)

const obj = JSON.parse(badJson); // ここで例外が発生して処理が止まる
JavaScript

業務コードでこれをそのまま使うと、

画面が真っ白になる
API 処理が 500 エラーで落ちる
バッチ処理が途中で止まる

といった「一発アウト」の事故につながります。
だからこそ、「安全な JSON parse」をユーティリティとして用意しておく価値が大きいのです。


基本形:try/catch で例外を握りつぶさない「安全ラッパー」

安全な JSON parse の第一歩は、「JSON.parse を直接呼ばず、必ず try/catch で包む」ことです。

いちばんシンプルな safeJsonParse

function safeJsonParse(text) {
  try {
    return {
      ok: true,
      value: JSON.parse(text),
    };
  } catch (error) {
    return {
      ok: false,
      error,
    };
  }
}
JavaScript

使い方はこうです。

const good = safeJsonParse('{"id":1,"name":"山田"}');
const bad  = safeJsonParse('{id:1,"name":"山田"}');

if (good.ok) {
  console.log("成功:", good.value);
} else {
  console.log("失敗:", good.error);
}

if (bad.ok) {
  console.log("成功:", bad.value);
} else {
  console.log("失敗:", bad.error.message);
}
JavaScript

重要なのは、「例外を外に飛ばさず、結果を“成功か失敗か”の形で返している」ことです。
これにより、呼び出し側は「落ちるかもしれない関数」ではなく、「結果を見て分岐すればよい関数」として扱えます。


デフォルト値を返すバージョン

「失敗したときはエラー情報はいらないから、代わりにデフォルト値を返してほしい」という場面も多いです。
その場合は、次のようなユーティリティが便利です。

function safeJsonParseOr(text, defaultValue) {
  try {
    return JSON.parse(text);
  } catch {
    return defaultValue;
  }
}
JavaScript

使い方の例です。

const json1 = '{"theme":"dark"}';
const json2 = '壊れたJSON';

const settings1 = safeJsonParseOr(json1, {});
const settings2 = safeJsonParseOr(json2, { theme: "light" });

console.log(settings1); // { theme: "dark" }
console.log(settings2); // { theme: "light" } (失敗したのでデフォルト)
JavaScript

ここでのポイントは、「呼び出し側が try/catch を書かなくてよくなる」ことです。
「壊れていたらこの値にしておいて」という意図を、関数の引数で表現できます。


型を意識した「期待する形かどうか」のチェック

JSON.parse が成功しても、「中身が期待した形とは限らない」という問題があります。

例えば、次のようなケースです。

const json = '"これは文字列です"'; // JSON としては正しい

const result = JSON.parse(json);
console.log(result); // "これは文字列です"
JavaScript

もしあなたが「オブジェクトが来るはず」と思っていたら、ここで result.id を読もうとしてエラーになります。
つまり、「パースに成功したか」と「中身の構造が正しいか」は別問題です。

そこで、「パース成功+型チェック」をセットにしたユーティリティを考えます。

例:オブジェクトであることを期待する safeJsonObjectParse

function safeJsonObjectParse(text) {
  try {
    const value = JSON.parse(text);
    if (value === null || typeof value !== "object" || Array.isArray(value)) {
      return { ok: false, error: new Error("オブジェクトではありません") };
    }
    return { ok: true, value };
  } catch (error) {
    return { ok: false, error };
  }
}
JavaScript

使い方の例です。

const json1 = '{"id":1,"name":"山田"}';
const json2 = '"ただの文字列"';

const r1 = safeJsonObjectParse(json1);
const r2 = safeJsonObjectParse(json2);

console.log(r1.ok, r1.value); // true, { id: 1, name: "山田" }
console.log(r2.ok, r2.error.message); // false, "オブジェクトではありません"
JavaScript

ここでは、「JSON として正しいか」に加えて、「オブジェクトであるかどうか」もチェックしています。
業務では、「ここでは必ずオブジェクトが来るはず」「ここでは配列が来るはず」といった前提が多いので、
こうした「型付きの safe parse」はかなり役に立ちます。


実務でよくあるパターン別の使い方

ローカルストレージからの読み込み

localStorage には文字列しか保存できないので、オブジェクトを保存するときは JSON にします。

localStorage.setItem("settings", JSON.stringify({ theme: "dark" }));
JavaScript

読み込むときに、壊れた値や空文字が入っていると JSON.parse が落ちます。
そこで、安全な読み込み関数を用意します。

function loadSettings() {
  const text = localStorage.getItem("settings");
  if (text == null) {
    return { theme: "light" }; // 初期値
  }

  return safeJsonParseOr(text, { theme: "light" });
}

const settings = loadSettings();
console.log(settings.theme);
JavaScript

これで、「保存されている JSON が壊れていても、アプリ全体が落ちることはない」状態になります。

API レスポンスの一部が JSON 文字列になっている場合

API が「中のフィールドだけ JSON 文字列」という形で返してくることがあります。

// 例: data.meta が JSON 文字列
const data = {
  id: 1,
  meta: '{"tags":["a","b"],"published":true}',
};
JavaScript

このときも、直接 JSON.parse せず、安全なラッパーを通します。

const metaResult = safeJsonParse(data.meta);

if (!metaResult.ok) {
  console.error("meta の JSON が壊れています", metaResult.error);
} else {
  console.log("タグ:", metaResult.value.tags);
}
JavaScript

「壊れていたらログを出してスキップする」「壊れていたらデフォルト値にする」など、
プロジェクトの方針に合わせて振る舞いを決められます。


どこまで「安全」にするかを決める視点

安全な JSON parse を設計するとき、次のような軸で考えると整理しやすくなります。

例外を外に出さないか(常に戻り値で表現するか)
失敗時にエラー情報を返すか、デフォルト値だけ返すか
中身の型(オブジェクト・配列・特定の構造)までチェックするか

例えば、「バッチ処理で壊れた JSON があったら、そのレコードだけスキップしてログに残したい」なら、
{ ok: boolean, value?, error? } 形式が向いています。

一方、「設定ファイルが壊れていたら即座に初期値にフォールバックしたい」なら、
safeJsonParseOr(text, defaultValue) のような形が使いやすいです。


小さな練習で感覚をつかむ

次のような文字列を用意して、自分で safeJsonParsesafeJsonParseOr を実装して試してみてください。

const samples = [
  '{"id":1,"name":"山田"}',
  '["a","b","c"]',
  '"ただの文字列"',
  '{id:1,"name":"山田"}', // 不正
  '壊れたJSON',
  '',
  '   ',
];
JavaScript

それぞれに対して、「成功か失敗か」「失敗時にどう扱うか(ログ?デフォルト?無視?)」を決めていくと、
あなたのプロジェクトにとってちょうどいい「安全な JSON parse ユーティリティ」の形が見えてきます。

タイトルとURLをコピーしました