JavaScript JSON loading guide with fetch and JSON.parse pipeline for HTML rendering
目次

1. JSON読み込みで最初に押さえるポイント

JavaScriptでJSONを読み込むときは、読み込み元が「HTTPで取得するデータ」なのか「すでに手元にある文字列」なのかを先に分けます。外部APIやJSONファイルを読むならfetch()response.json()、文字列をJavaScriptの値へ変換するならJSON.parse()を使います。

この記事では、fetch()response.json()JSON.parse()、HTML表示、エラー処理、ローカル開発時の注意点までを、実務で迷わない順番で整理します。

1.1 JSONはJavaScriptオブジェクトそのものではない

結論:JSONはテキスト形式のデータであり、JavaScriptのオブジェクトリテラルと似ていますが同じものではありません。

JSONではキーと文字列をダブルクォーテーションで囲み、末尾カンマやコメントは使えません。次のJSONは正しい形式です。

{
  "name": "Taro",
  "age": 30,
  "skills": ["JavaScript", "HTML", "CSS"],
  "active": true
}

一方、次のような書き方はJavaScriptのオブジェクトとしては使える場合がありますが、JSONとしては不正です。

{
  name: "Taro",
  age: 30,
}

JSONを読み込む処理でエラーが出る場合、通信よりも先に「JSONとして正しい構文か」を確認すると原因を切り分けやすくなります。

1.2 読み込み方法は2種類に分けて考える

結論:URLから取るならfetch()、文字列を変換するならJSON.parse()です。

よく使う方法は次の2つです。

やりたいこと使うAPI代表例
APIやJSONファイルをHTTPで取得するfetch()response.json()/api/users./data.json
JSON文字列をJavaScriptの値へ変換するJSON.parse()"{\"name\":\"Taro\"}"

fetch()は通信を行うため非同期処理になります。JSON.parse()は手元にある文字列を解析する同期処理です。この違いを混同すると、Promiseの扱いやエラー処理でつまずきます。

2. fetchでJSONファイルやAPIを読み込む

fetch()はブラウザ標準のAPIで、指定したURLへリクエストを送り、Responseオブジェクトを返します。JSON本文をJavaScriptの値として取り出すには、返ってきたResponseに対してresponse.json()を呼び出します。

JavaScript fetch response.json JSON loading flow from API response to rendered HTML content

2.1 最小構成のコード

結論:await fetch(url)でレスポンスを受け取り、await response.json()でJSON本文を解析します。

async function loadUser() {
  const response = await fetch("/api/user.json");
  const user = await response.json();

  console.log(user.name);
}

loadUser();

このコードでは、fetch()がHTTPレスポンスを取得し、response.json()がレスポンス本文をJSONとして読み取ります。response.json()も非同期処理なので、awaitを付ける必要があります。

2.2 HTTPエラーは自動で例外にならない

結論:fetch()は404や500のレスポンスだけではcatchへ移りません。response.okを自分で確認します。

初心者がよく誤解する点として、fetch()はサーバーからHTTPレスポンスを受け取れた場合、ステータスが404でも500でも通常は解決済みのPromiseになります。ネットワーク障害、CORSエラー、リクエストの中断などでは例外になりますが、HTTPステータスの判定は別です。

async function loadJson(url) {
  const response = await fetch(url);

  if (!response.ok) {
    throw new Error(`HTTP error: ${response.status}`);
  }

  return response.json();
}

このようにしておくと、存在しないURLやサーバーエラーを正常データとして処理してしまう事故を防げます。

2.3 Content-Typeも確認すると安全

結論:APIがHTMLのエラーページを返すこともあるため、必要に応じてContent-Typeを確認します。

response.json()はレスポンス本文をJSONとして解析します。サーバー側の設定ミスでHTMLやプレーンテキストが返ってくると、SyntaxErrorが発生します。

async function loadJson(url) {
  const response = await fetch(url);

  if (!response.ok) {
    throw new Error(`HTTP error: ${response.status}`);
  }

  const contentType = response.headers.get("content-type");

  if (!contentType?.includes("application/json")) {
    throw new Error("Response is not JSON");
  }

  return response.json();
}

すべてのAPIが厳密にapplication/jsonを返すとは限りませんが、自分で管理しているAPIや業務システムでは確認しておくと不具合の発見が早くなります。

3. JSON.parseで文字列をオブジェクトに変換する

JSON.parse()は、JSON形式の文字列をJavaScriptの値へ変換するメソッドです。HTTP通信は行いません。localStorageに保存した文字列、フォームから受け取ったJSON文字列、サーバーから別の形式で受け取った文字列などを解析するときに使います。

3.1 JSON.parseの基本

結論:JSON.parse()に渡す文字列は、正しいJSONでなければなりません。

const jsonText = '{"name":"Taro","age":30}';
const user = JSON.parse(jsonText);

console.log(user.name);
console.log(user.age);

jsonTextは文字列です。JSON.parse(jsonText)を実行した後のuserはJavaScriptのオブジェクトになります。

3.2 SyntaxErrorをtry…catchで扱う

結論:ユーザー入力や外部データを解析する場合は、try...catchで構文エラーに備えます。

function parseJsonSafely(jsonText) {
  try {
    return {
      ok: true,
      data: JSON.parse(jsonText)
    };
  } catch (error) {
    return {
      ok: false,
      message: "JSONの形式が正しくありません"
    };
  }
}

const result = parseJsonSafely('{"name":"Taro"}');

if (result.ok) {
  console.log(result.data.name);
} else {
  console.error(result.message);
}

JSON.parse()は構文エラーがあるとSyntaxErrorを投げます。アプリ全体が止まらないよう、入力値や外部データを扱う箇所では失敗時の表示やログを用意します。

3.3 JSON.stringifyとの違い

結論:JSON.parse()は文字列から値へ、JSON.stringify()は値から文字列へ変換します。

メソッド変換方向主な用途
JSON.parse()JSON文字列からJavaScriptの値読み込み、復元、解析
JSON.stringify()JavaScriptの値からJSON文字列保存、送信、ログ出力

たとえばlocalStorageは文字列しか保存できないため、保存時はJSON.stringify()、読み込み時はJSON.parse()を使います。

const settings = {
  theme: "dark",
  pageSize: 20
};

localStorage.setItem("settings", JSON.stringify(settings));

const savedText = localStorage.getItem("settings");
const savedSettings = JSON.parse(savedText);

console.log(savedSettings.theme);

4. 取得したJSONをHTMLに表示する

JSONを読み込めても、画面に表示する段階で安全性やDOM操作を誤ると問題が起きます。特に外部データをそのままinnerHTMLへ入れる方法は、内容によっては危険です。

4.1 textContentで安全に表示する

結論:単純なテキスト表示にはtextContentを使います。

次のJSONをAPIから取得するとします。

{
  "name": "Taro",
  "role": "Frontend Developer"
}

HTML側には表示先を用意します。

<div id="profile"></div>

JavaScriptでは、取得した値をtextContentで代入します。

async function renderProfile() {
  const response = await fetch("/api/profile.json");

  if (!response.ok) {
    throw new Error(`HTTP error: ${response.status}`);
  }

  const profile = await response.json();
  const container = document.querySelector("#profile");

  container.textContent = `${profile.name} / ${profile.role}`;
}

renderProfile().catch((error) => {
  console.error(error);
});

textContentは文字列をHTMLとして解釈しないため、外部データを表示する基本手段として扱いやすいです。

4.2 リスト表示はcreateElementで組み立てる

結論:配列データを表示するときは、要素を作って追加すると安全で保守しやすくなります。

[
  { "id": 1, "name": "HTML" },
  { "id": 2, "name": "CSS" },
  { "id": 3, "name": "JavaScript" }
]
<ul id="skill-list"></ul>
async function renderSkills() {
  const response = await fetch("/api/skills.json");

  if (!response.ok) {
    throw new Error(`HTTP error: ${response.status}`);
  }

  const skills = await response.json();
  const list = document.querySelector("#skill-list");

  list.replaceChildren();

  for (const skill of skills) {
    const item = document.createElement("li");
    item.textContent = skill.name;
    list.append(item);
  }
}

renderSkills().catch(console.error);

replaceChildren()で既存の表示を消してから追加すると、再読み込み時に同じ項目が重複する問題を避けられます。

4.3 innerHTMLを使う場合の注意

結論:信頼できないJSONデータをinnerHTMLへ直接入れてはいけません。

次のようなコードは、外部データにHTMLやスクリプトが混ざったときに危険です。

container.innerHTML = data.description;

APIの値が完全に自分の管理下にあり、サーバー側で安全なHTMLだけを許可している場合を除き、表示にはtextContentやDOM生成を使うほうが安全です。

5. ローカルJSONファイルを読むときの注意点

手元のdata.jsonをブラウザで読み込もうとして失敗する場合、コードではなく実行環境が原因のことがあります。

5.1 file://ではfetchが失敗することがある

結論:HTMLファイルを直接開くのではなく、ローカルサーバー経由で確認します。

ブラウザでindex.htmlをダブルクリックして開くと、URLがfile://で始まります。この状態では、ブラウザのセキュリティ制限によりfetch("./data.json")が期待どおり動かないことがあります。

開発時は、プロジェクトのフォルダをHTTPサーバーで配信して確認します。

npx serve .

または、Pythonが使える環境なら次のように起動できます。

python -m http.server 8000

その後、ブラウザでhttp://localhost:8000のようなURLを開きます。

5.2 パスの基準を確認する

結論:fetch("./data.json")の基準は、実行中のHTMLページのURLです。

たとえば次の構成だとします。

project/
  index.html
  data/
    users.json

index.htmlから読み込む場合は、次のように書きます。

fetch("./data/users.json");

JavaScriptファイルの場所ではなく、ブラウザで表示しているページのURLを基準に考えると、パスの間違いを減らせます。ただし、ビルドツールやフレームワークを使う場合は、そのツールの公開ディレクトリ規則も確認してください。

6. よくあるエラーと対処法

JSON読み込みの不具合は、通信、HTTPステータス、JSON構文、CORS、DOM表示のどこで起きているかを分けると解決しやすくなります。

6.1 Unexpected tokenの原因

結論:Unexpected tokenは、JSONではない文字列をJSONとして解析したときによく出ます。

たとえば、APIがログインページのHTMLを返しているのにresponse.json()を呼ぶと、HTMLの先頭文字で構文エラーになることがあります。

async function debugJson(url) {
  const response = await fetch(url);
  const text = await response.text();

  console.log(text.slice(0, 200));

  return JSON.parse(text);
}

原因調査では、一時的にresponse.text()で本文の先頭を確認すると、HTMLエラー、空レスポンス、余計な文字列混入などを見つけやすくなります。確認後は、通常のresponse.json()を使う形へ戻します。

6.2 CORSエラーの原因

結論:CORSはブラウザ側の制限で、基本的にはサーバー側のレスポンスヘッダー設定が必要です。

別ドメインのAPIをブラウザから読む場合、APIサーバーが許可していないオリジンからのアクセスはブロックされます。フロントエンド側のJavaScriptだけで任意に解除することはできません。

開発時の対処は次のどれかです。

状況対処
自分でAPIを管理しているサーバー側で許可するオリジンを設定する
外部APIを使っている公式の利用方法、APIキー、許可ドメインを確認する
開発環境だけで失敗する開発サーバーのプロキシ設定を使う

CORSエラーを避けるためにブラウザの安全機能を無効化して確認する方法は、通常の開発手順としては推奨できません。

6.3 response.json is not a functionの原因

結論:response.json()fetch()が返すResponseオブジェクトのメソッドです。

次のように、すでにJSON化された値に対してjson()を呼ぶと失敗します。

const response = await fetch("/api/user.json");
const data = await response.json();

data.json(); // 誤り

response.json()を呼ぶのはレスポンス本文を読み取る一度だけです。読み取った後のdataは通常のオブジェクトや配列として扱います。

6.4 body stream already readの原因

結論:レスポンス本文は原則として一度だけ読み取れます。

次のコードは、同じレスポンス本文をtext()json()で二重に読もうとしているため失敗します。

const response = await fetch("/api/user.json");

const text = await response.text();
const data = await response.json(); // 誤り

調査で本文を文字列として確認したい場合は、textJSON.parse()へ渡します。

const response = await fetch("/api/user.json");
const text = await response.text();
const data = JSON.parse(text);

通常の処理では、await response.json()だけを使えば十分です。

7. 実務向けの読み込み関数を作る

同じエラー処理を何度も書くと抜け漏れが出ます。小さな共通関数にまとめておくと、画面側のコードが読みやすくなります。

7.1 再利用できるloadJson関数

結論:HTTPステータス、Content-Type、JSON解析をまとめて扱います。

async function loadJson(url, options = {}) {
  const response = await fetch(url, options);

  if (!response.ok) {
    throw new Error(`HTTP error: ${response.status}`);
  }

  const contentType = response.headers.get("content-type");

  if (contentType && !contentType.includes("application/json")) {
    throw new Error(`Expected JSON but received: ${contentType}`);
  }

  return response.json();
}

使う側はシンプルになります。

async function main() {
  try {
    const users = await loadJson("/api/users.json");
    console.log(users);
  } catch (error) {
    console.error("JSONの読み込みに失敗しました", error);
  }
}

main();

Content-TypeがないAPIも存在するため、上の例ではヘッダーがある場合だけ検査しています。自分で管理するAPIなら、常にapplication/jsonを返すように統一すると保守しやすくなります。

7.2 タイムアウトや中断を扱う

結論:AbortControllerを使うと、長すぎるリクエストを中断できます。

async function loadJsonWithTimeout(url, timeoutMs = 5000) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => {
    controller.abort();
  }, timeoutMs);

  try {
    return await loadJson(url, {
      signal: controller.signal
    });
  } finally {
    clearTimeout(timeoutId);
  }
}

通信が遅いときに画面が待ち続けると、ユーザーには何が起きているか分かりません。タイムアウト、再試行、エラーメッセージを設計しておくと、実運用で扱いやすくなります。

7.3 POSTでJSONを送信する場合

結論:送信時はJSON.stringify()で本文を作り、Content-Typeを指定します。

JSONの読み込みだけでなく、APIへJSONを送る場面もよくあります。

async function createUser(user) {
  const response = await fetch("/api/users", {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify(user)
  });

  if (!response.ok) {
    throw new Error(`HTTP error: ${response.status}`);
  }

  return response.json();
}

createUser({
  name: "Taro",
  role: "Frontend Developer"
}).catch(console.error);

送信時はJSON.stringify()、受信時はresponse.json()という対応で覚えると整理しやすくなります。

8. 公式情報で確認したい項目

JavaScriptのAPIはブラウザや実行環境によって対応状況が異なる場合があります。挙動を確認するときは、英語の公式リファレンスを最小限参照すると正確です。

8.1 fetchとResponse.json

結論:fetch()はHTTPリクエストを行い、response.json()はレスポンス本文をJSONとして読み取ります。

仕様やブラウザ対応を確認したい場合は、次のリファレンスが役立ちます。

MDN: Using the Fetch API

MDN: Response.json()

8.2 JSON.parse

結論:JSON.parse()はJSON文字列をJavaScriptの値へ変換します。

例外の種類や第2引数のreviverを確認したい場合は、次のリファレンスを参照します。

MDN: JSON.parse()

通常の記事やアプリ開発では、まず本文で紹介した基本形、HTTPエラー処理、try...catchを押さえれば十分です。

9. まとめ

JavaScriptでJSONを読み込む基本は、URLから取得する場合はfetch()response.json()、文字列を解析する場合はJSON.parse()です。実務では、response.okでHTTPエラーを確認し、必要に応じてContent-Typeを検査し、画面表示ではtextContentやDOM生成を使うと安全に扱えます。

JSON読み込みで詰まったときは、通信できているか、HTTPステータスは成功か、返ってきた本文は本当にJSONか、表示処理でHTMLとして解釈していないかを順番に確認してください。この順番で切り分けると、Unexpected token、CORS、response.json is not a functionのようなエラーにも対応しやすくなります。

10. FAQ

10.1 JavaScriptでJSONを読み込む一番基本の方法は何ですか?

結論:APIやJSONファイルを読むならfetch()で取得し、response.json()でJavaScriptの値へ変換する方法が基本です。

10.2 JSON.parseとresponse.jsonの違いは何ですか?

結論:JSON.parse()は手元のJSON文字列を解析し、response.json()fetch()で得たレスポンス本文をJSONとして非同期に読み取ります。

10.3 fetchで404なのにcatchされないのはなぜですか?

結論:fetch()はHTTPレスポンスを受け取れた場合、404や500だけでは例外にしないため、response.okを自分で確認する必要があります。

10.4 ローカルのdata.jsonを読み込めない原因は何ですか?

結論:file://でHTMLを直接開いている場合、ブラウザの制限で失敗することがあるため、ローカルサーバー経由で確認します。

10.5 取得したJSONをHTMLに表示するときの注意点は何ですか?

結論:外部データをinnerHTMLへ直接入れると危険な場合があるため、単純な文字表示にはtextContent、リスト表示にはcreateElement()を使うのが安全です。