
JavaScriptにおけるJSONの基礎
2025-07-28JSONとは
JSON(JavaScript Object Notation)は、軽量なデータ交換フォーマットです。JavaScriptのオブジェクト表記法に基づいていますが、独立したデータ形式として多くのプログラミング言語でサポートされています。
JSONの特徴
- テキストベース:人間が読める形式
- 軽量:XMLなどに比べてデータサイズが小さい
- 言語非依存:多くのプログラミング言語で扱える
- JavaScriptと親和性が高い:簡単に相互変換可能
JSONの基本構造
有効なJSONデータ型
- 文字列:
" "
で囲む
"Hello, World!"
- 数値:整数または浮動小数点数
42
3.14159
- 真偽値:
true
false
- null:
null
- 配列:
[]
で囲む
[1, 2, 3, "four", true]
- オブジェクト:
{}
で囲み、"key": value
のペアで構成
{
"name": "山田太郎",
"age": 30,
"isStudent": false
}
無効なJSON表現
- 関数やundefinedは使用不可
- コメントはサポートされていない
- キーは必ずダブルクォートで囲む必要がある
- 末尾のカンマが許されない
JavaScriptでのJSON操作
JSON文字列 ⇄ JavaScriptオブジェクトの変換
JSON.parse() – 文字列からオブジェクトへ
const jsonString = '{"name":"山田太郎","age":30}';
const obj = JSON.parse(jsonString);
console.log(obj.name); // "山田太郎"
JSON.stringify() – オブジェクトから文字列へ
const user = {
name: "山田太郎",
age: 30,
hobbies: ["読書", "プログラミング"]
};
const jsonString = JSON.stringify(user);
console.log(jsonString);
// '{"name":"山田太郎","age":30,"hobbies":["読書","プログラミング"]}'
オプションパラメータ
JSON.stringify()の第2引数(リプレーサー)
// 特定のプロパティのみ含める
const json = JSON.stringify(user, ["name", "age"]);
console.log(json); // '{"name":"山田太郎","age":30}'
// 関数で変換を制御
const json = JSON.stringify(user, (key, value) => {
if (key === "age") return undefined; // ageプロパティを除外
return value;
});
第3引数(スペース数)
// 整形出力(人間が読みやすい形式)
const prettyJson = JSON.stringify(user, null, 2);
console.log(prettyJson);
/*
{
"name": "山田太郎",
"age": 30,
"hobbies": [
"読書",
"プログラミング"
]
}
*/
カスタムtoJSONメソッド
const person = {
name: "山田太郎",
age: 30,
toJSON() {
return {
fullName: this.name,
years: this.age
};
}
};
console.log(JSON.stringify(person));
// '{"fullName":"山田太郎","years":30}'
JSONの実践的な使用例
API通信でのJSON
JSONファイルは、API連携においてデータのやり取りを行う標準的なフォーマットとして使われます。APIから取得した情報はJSON形式で送信され、クライアント側でパースして利用します。例えば、天気情報APIからは気温や湿度などのデータがJSONで返され、Webアプリやスマホアプリがその情報を解析し、画面に表示します。軽量で人間にも読みやすく、ほとんどのプログラミング言語で簡単に扱えるため、API通信の中心的役割を担っています。
// APIからデータ取得
fetch('https://api.example.com/users')
.then(response => response.json()) // JSONをパース
.then(data => console.log(data))
.catch(error => console.error(error));
// データ送信
const newUser = { name: "花子", age: 25 };
fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(newUser)
});
ローカルストレージでの利用
JSON形式は、ブラウザのローカルストレージにデータを保存する際によく使われます。オブジェクトや配列などの複雑なデータをJSON.stringify()
で文字列化して保存し、必要なときにJSON.parse()
で元の構造に戻せるため、ユーザー設定や一時データの保持に便利です。
// 保存
const settings = { theme: "dark", fontSize: 14 };
localStorage.setItem('userSettings', JSON.stringify(settings));
// 読み取り
const savedSettings = JSON.parse(localStorage.getItem('userSettings'));
console.log(savedSettings.theme); // "dark"
設定ファイルとしてのJSON
JSONは、アプリケーションやシステムの設定情報を記述する形式としても広く利用されます。軽量で可読性が高く、プログラムから容易に読み込めるため、環境設定やカスタマイズ項目を管理する際に適しています。特にWebアプリやNode.jsアプリの設定ファイル(例:config.json
)で一般的です。
// config.json
{
"apiUrl": "https://api.example.com",
"maxRetries": 3,
"timeout": 5000
}
// JavaScriptで読み込み
fetch('config.json')
.then(response => response.json())
.then(config => {
console.log(`API URL: ${config.apiUrl}`);
});
JSON Schemaによる検証
JSON Schemaによる検証とは、JSONデータが特定のルールや構造に従っているかを確認するための仕組みです。JSON SchemaはJSON形式で記述され、次のような項目を定義できます。
- 型の指定(例: string, number, boolean, object, array)
- 必須プロパティの指定(
required
) - 値の範囲や文字数の制限(
minimum
,maximum
,minLength
,maxLength
) - 列挙型の指定(
enum
で特定の値のみ許可) - ネストしたオブジェクトや配列の構造定義
// person.schema.json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "number", "minimum": 0 }
},
"required": ["name"]
}
これにより、以下の利点があります。
- バグの早期発見やメンテナンス性の向上につながる
- APIレスポンスが仕様通りかどうかを自動的にチェックできる
- 外部から受け取ったデータの安全性を確保できる
セキュリティ上の注意点
- JSONインジェクション:
eval()
を使用しない
JSONインジェクションとは、アプリケーションが外部から受け取ったデータを適切に検証・エスケープせずに JSON に組み込むことで、悪意あるコードや構造を注入されてしまう脆弱性です。これにより、不正なデータの改ざんや情報漏えい、予期せぬ処理の実行などが発生する可能性があります。
// 危険な例
const obj = eval('(' + jsonString + ')');
// 安全な例
const obj = JSON.parse(jsonString);
- 巨大JSONの処理:ストリーミング処理を検討
stream-json
というライブラリを使った安全なJSONストリーミング解析例
stream-json
は、Node.js で巨大な JSON データをストリーミング処理できるライブラリです。
一度に全てをメモリに読み込まず、少しずつパースしながら処理できるため、大容量データでも高速かつ省メモリで扱えます。
const fs = require('fs');
const { parser } = require('stream-json');
const { streamArray } = require('stream-json/streamers/StreamArray');
const jsonStream = fs.createReadStream('huge.json')
.pipe(parser())
.pipe(streamArray());
jsonStream.on('data', ({ key, value }) => {
// 要素ごとに安全に処理できる
console.log(`Key: ${key}, Value:`, value);
});
jsonStream.on('end', () => {
console.log('JSON parsing complete.');
});
- 循環参照:
JSON.stringify()
でエラーが発生
オブジェクト内で自分自身を参照している(または互いに参照し合っている)と、JSON.stringify()
はエラーになります。
const obj = { a: 1 };
obj.self = obj;
JSON.stringify(obj); // TypeError
このように循環していると、JSON.stringify()
はどこまでたどればいいか分からず無限ループになるため、エラーになります。解決するには、循環部分を除外するか、flatted
や circular-json
のような循環参照対応ライブラリを使います。
パフォーマンス最適化
- 大きなJSON:分割処理やストリーミングを検討
- 頻繁な操作:一度パースしてキャッシュ
- Web Worker:メインスレッドをブロックしない
演習問題
初級問題(3問)
- 次のJavaScriptオブジェクトをJSON文字列に変換しなさい。
const book = {
title: "JavaScript入門",
price: 2500,
inStock: true
};
- 次のJSON文字列をJavaScriptオブジェクトに変換し、
author
プロパティにアクセスしなさい。
{
"title": "JSONガイド",
"author": "山田太郎",
"published": false
}
- 次のJSONのどこが間違っているか指摘しなさい。
{
name: "エラー例",
"values": [1, 2, 3],
"active": true,
}
中級問題(6問)
- 次のオブジェクトをJSONに変換する際、
password
プロパティが含まれないようにしなさい。
const user = {
username: "js_user",
password: "secret123",
email: "user@example.com"
};
- 次のJSON配列から、
age
が30以上の人のname
だけを含む配列を作成しなさい。
[
{ "name": "太郎", "age": 25 },
{ "name": "花子", "age": 30 },
{ "name": "次郎", "age": 35 }
]
- 深い階層を持つ次のJSONから、
street
の値を取得しなさい。
{
"user": {
"name": "山田太郎",
"address": {
"city": "東京",
"street": "渋谷区1-2-3"
}
}
}
- 次の2つのJSONオブジェクトをマージしなさい(競合時はobj2の値が優先される)。
const obj1 = { "a": 1, "b": 2 };
const obj2 = { "b": 3, "c": 4 };
- 次のJSON文字列を整形(インデント2スペース)してコンソールに出力しなさい。
{"name":"整形例","values":[1,2,3],"active":true}
- 次のコードの出力結果とその理由を説明しなさい。
const obj = { date: new Date() };
console.log(JSON.stringify(obj));
上級問題(3問)
- 次のような循環参照を持つオブジェクトをJSON文字列に変換するカスタム関数を作成しなさい(循環参照は
"[Circular]"
という文字列に変換)。
const obj = { a: 1 };
obj.self = obj;
- 巨大なJSONファイルをストリーミング処理するNode.jsのコード例を書きなさい(ファイルから読み取り、パースしながら処理)。
- JSON Schemaを使用して、次の要件を満たすスキーマを定義しなさい。
- オブジェクト型
- 必須プロパティ: id(数値), name(文字列)
- オプションプロパティ: tags(文字列の配列、最大5個まで)
- idは正の整数
解答例
初級問題解答
- JSON文字列に変換
const jsonString = JSON.stringify(book);
// '{"title":"JavaScript入門","price":2500,"inStock":true}'
- JSONをオブジェクトに変換
const jsonObj = JSON.parse('{"title":"JSONガイド","author":"山田太郎","published":false}');
console.log(jsonObj.author); // "山田太郎"
- JSONの間違い
name
がダブルクォートで囲まれていない- 最後のプロパティに余分なカンマがある
{
"name": "エラー例", // nameもダブルクウォートで囲む
"values": [1, 2, 3],
"active": true // 最後のプロパティはカンマなし
}
中級問題解答
- passwordを除外
const json = JSON.stringify(user, (key, val) =>
key === "password" ? undefined : val
);
- ageが30以上の人のnameだけを含む配列
const people = JSON.parse('[{"name":"太郎","age":25},{"name":"花子","age":30},{"name":"次郎","age":35}]');
const names = people.filter(p => p.age >= 30).map(p => p.name);
- streetの値を取得
const data = JSON.parse('{"user":{"name":"山田太郎","address":{"city":"東京","street":"渋谷区1-2-3"}}}');
const street = data.user.address.street;
- オブジェクトのマージ
const merged = JSON.stringify({ ...obj1, ...obj2 });
// '{"a":1,"b":3,"c":4}'
- JSON文字列を整形(インデント2スペース)してコンソールに出力
const jsonObj = JSON.parse('{"name":"整形例","values":[1,2,3],"active":true}');
console.log(JSON.stringify(jsonObj, null, 2));
- 出力結果とその理由を説明
{“date”:”2023-05-01T00:00:00.000Z”}
const obj = { date: new Date() };
console.log(JSON.stringify(obj)); // {"date":"2023-10-01T00:00:00.000Z"}
// DateオブジェクトはJSON.stringify()で文字列に変換される
上級問題解答
- 循環参照を処理する関数
function safeStringify(obj) {
const seen = new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) return "[Circular]";
seen.add(value);
}
return value;
});
}
- ストリーミング処理の例(Node.js)
const fs = require('fs');
const { parse } = require('JSONStream');
const stream = fs.createReadStream('large.json') .pipe(parse('*'));
stream.on('data', data => { // データを逐次処理 console.log(data); });
- JSON Schemaの例
json {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"id": {
"type": "integer",
"minimum": 1
},
"name": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 5
}
},
"required": ["id", "name"]
}