JSON (Native)
ライブラリ
JSON (Native)
概要
JSON (JavaScript Object Notation)は、JavaScript標準のシリアライゼーション機能として提供される軽量なデータ交換形式です。JSON.stringify()とJSON.parse()による簡潔なAPIで、JavaScriptオブジェクトをJSON文字列に変換し、その逆変換を行います。すべてのJavaScript環境で標準で利用可能で、外部ライブラリを必要とせず、WebアプリケーションとAPIのデータ交換において最も広く使用されています。
詳細
JSON NativeはECMAScript 5仕様として標準化され、現在では全てのモダンブラウザとNode.jsで標準サポートされています。可読性の高いテキスト形式で、HTTP通信やファイル保存、設定ファイルなど様々な用途で活用されます。シンプルなAPIながら、replacer関数やspace引数による柔軟な出力制御、reviver関数による解析時のカスタマイズが可能です。
主な特徴
- 標準サポート: 全てのJavaScript環境で追加ライブラリ不要
- 軽量性: ゼロオーバーヘッドで高速動作
- 可読性: 人間が読みやすいテキスト形式
- 互換性: 多言語・多プラットフォーム間での高い互換性
- 柔軟性: replacer/reviver関数による変換処理のカスタマイズ
- Web標準: RESTful APIとWebアプリケーションのデファクトスタンダード
メリット・デメリット
メリット
- JavaScript環境で外部ライブラリが不要(バンドルサイズ0KB)
- 学習コストが低く、直感的なAPI設計
- 多言語・多プラットフォームでの優れた互換性
- 人間が読み書きしやすいテキスト形式
- HTTPプロトコルとの親和性が高い
- デバッグとログ出力が容易
デメリット
- バイナリデータの効率的な表現が不可
- 循環参照オブジェクトの処理不可
- 日付型や関数などJavaScript固有型の標準サポートなし
- バイナリ形式と比較してファイルサイズが大きい
- セキュリティ面でJSONインジェクション攻撃のリスク
- 大容量データでのパフォーマンス制約
参考ページ
書き方の例
基本的なシリアライゼーション
// 基本的なオブジェクトの変換
const userObject = {
id: 123,
name: '田中太郎',
email: '[email protected]',
isActive: true,
lastLogin: null
};
// オブジェクトをJSON文字列に変換
const jsonString = JSON.stringify(userObject);
console.log(jsonString);
// 出力: {"id":123,"name":"田中太郎","email":"[email protected]","isActive":true,"lastLogin":null}
// JSON文字列をオブジェクトに変換
const parsedObject = JSON.parse(jsonString);
console.log(parsedObject);
// 出力: { id: 123, name: '田中太郎', email: '[email protected]', isActive: true, lastLogin: null }
// 配列の変換
const numbersArray = [1, 2, 3, 4, 5];
const arrayJson = JSON.stringify(numbersArray);
console.log(arrayJson); // 出力: "[1,2,3,4,5]"
const parsedArray = JSON.parse(arrayJson);
console.log(parsedArray); // 出力: [1, 2, 3, 4, 5]
高度な設定とカスタマイズ
// replacer関数を使用した出力制御
const sensitiveData = {
username: 'user123',
password: 'secret123',
email: '[email protected]',
apiKey: 'api_key_secret',
publicInfo: 'this is public'
};
// 機密情報を除外するreplacer関数
function secureReplacer(key, value) {
// パスワードやAPIキーを除外
if (key === 'password' || key === 'apiKey') {
return undefined;
}
return value;
}
const secureJson = JSON.stringify(sensitiveData, secureReplacer);
console.log(secureJson);
// 出力: {"username":"user123","email":"[email protected]","publicInfo":"this is public"}
// 特定のプロパティのみを含める
const allowedKeys = ['username', 'email'];
const filteredJson = JSON.stringify(sensitiveData, allowedKeys);
console.log(filteredJson);
// 出力: {"username":"user123","email":"[email protected]"}
// 整形された出力(space引数)
const prettyJson = JSON.stringify(userObject, null, 2);
console.log(prettyJson);
/*
{
"id": 123,
"name": "田中太郎",
"email": "[email protected]",
"isActive": true,
"lastLogin": null
}
*/
// reviver関数を使用した解析時の変換
const dateJson = '{"name":"太郎","created":"2025-01-01T00:00:00.000Z","count":"42"}';
function dateReviver(key, value) {
// ISO日付文字列をDateオブジェクトに変換
if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(value)) {
return new Date(value);
}
// 数値文字列を数値に変換
if (key === 'count' && typeof value === 'string' && !isNaN(value)) {
return parseInt(value, 10);
}
return value;
}
const revivedObject = JSON.parse(dateJson, dateReviver);
console.log(revivedObject);
// 出力: { name: '太郎', created: 2025-01-01T00:00:00.000Z, count: 42 }
console.log(revivedObject.created instanceof Date); // true
パフォーマンス最適化
// 大容量データの効率的な処理
function createLargeData(size) {
const data = [];
for (let i = 0; i < size; i++) {
data.push({
id: i,
name: `user_${i}`,
value: Math.random(),
timestamp: Date.now()
});
}
return data;
}
// パフォーマンス測定関数
function measurePerformance(label, fn) {
const start = performance.now();
const result = fn();
const end = performance.now();
console.log(`${label}: ${(end - start).toFixed(2)}ms`);
return result;
}
// 大量データの処理測定
const largeData = createLargeData(10000);
const jsonString = measurePerformance('Stringify 10,000 objects', () => {
return JSON.stringify(largeData);
});
const parsedData = measurePerformance('Parse large JSON', () => {
return JSON.parse(jsonString);
});
console.log(`JSON size: ${(jsonString.length / 1024).toFixed(2)} KB`);
// チャンク処理による大容量データの分割処理
function processInChunks(data, chunkSize = 1000) {
const chunks = [];
for (let i = 0; i < data.length; i += chunkSize) {
const chunk = data.slice(i, i + chunkSize);
chunks.push(JSON.stringify(chunk));
}
return chunks;
}
const chunks = measurePerformance('Chunk processing', () => {
return processInChunks(largeData);
});
console.log(`Processed ${chunks.length} chunks`);
型安全性とバリデーション
// JSON スキーマ風のバリデーション
function validateUserObject(obj) {
const requiredFields = ['id', 'name', 'email'];
const errors = [];
// 必須フィールドの検証
for (const field of requiredFields) {
if (!(field in obj)) {
errors.push(`Missing required field: ${field}`);
}
}
// 型検証
if (typeof obj.id !== 'number') {
errors.push('id must be a number');
}
if (typeof obj.name !== 'string') {
errors.push('name must be a string');
}
if (typeof obj.email !== 'string' || !obj.email.includes('@')) {
errors.push('email must be a valid email string');
}
return {
isValid: errors.length === 0,
errors
};
}
// 安全なJSON解析関数
function safeJsonParse(jsonString, validator = null) {
try {
const parsed = JSON.parse(jsonString);
if (validator) {
const validation = validator(parsed);
if (!validation.isValid) {
throw new Error(`Validation failed: ${validation.errors.join(', ')}`);
}
}
return { success: true, data: parsed };
} catch (error) {
return { success: false, error: error.message };
}
}
// 使用例
const validJson = '{"id":123,"name":"田中太郎","email":"[email protected]"}';
const invalidJson = '{"name":"田中太郎","email":"invalid-email"}';
const validResult = safeJsonParse(validJson, validateUserObject);
console.log('Valid result:', validResult);
const invalidResult = safeJsonParse(invalidJson, validateUserObject);
console.log('Invalid result:', invalidResult);
// TypeScript風の型チェック(実行時)
function isUserType(obj) {
return (
typeof obj === 'object' &&
obj !== null &&
typeof obj.id === 'number' &&
typeof obj.name === 'string' &&
typeof obj.email === 'string'
);
}
function parseUserJson(jsonString) {
const result = safeJsonParse(jsonString);
if (result.success && isUserType(result.data)) {
return result.data;
}
throw new Error('Invalid user JSON format');
}
エラーハンドリング
// 堅牢なJSONパース処理
function robustJsonParse(input, options = {}) {
const {
fallback = null,
logErrors = false,
attemptRepair = false
} = options;
// 入力値検証
if (typeof input !== 'string') {
if (logErrors) console.error('Input is not a string:', typeof input);
return fallback;
}
if (input.trim() === '') {
if (logErrors) console.error('Input is empty string');
return fallback;
}
try {
return JSON.parse(input);
} catch (error) {
if (logErrors) {
console.error('JSON parse error:', {
message: error.message,
input: input.substring(0, 100) + (input.length > 100 ? '...' : ''),
position: error.message.match(/position (\d+)/)?.[1]
});
}
// 自動修復の試行
if (attemptRepair) {
try {
// 末尾カンマの除去
let repairedInput = input.replace(/,(\s*[}\]])/g, '$1');
// 不正なクォートの修正
repairedInput = repairedInput.replace(/'/g, '"');
const repaired = JSON.parse(repairedInput);
if (logErrors) console.log('JSON repair successful');
return repaired;
} catch (repairError) {
if (logErrors) console.error('JSON repair failed:', repairError.message);
}
}
return fallback;
}
}
// 使用例
const malformedJson = `{
"name": "テスト",
"items": [1, 2, 3,],
"active": true,
}`;
const result = robustJsonParse(malformedJson, {
fallback: {},
logErrors: true,
attemptRepair: true
});
console.log('Parsed result:', result);
// ストリーミングJSONパーサー(大容量ファイル対応)
function parseJsonStream(jsonString, onObject, onError) {
let depth = 0;
let currentObject = '';
let inString = false;
let escaped = false;
for (let i = 0; i < jsonString.length; i++) {
const char = jsonString[i];
currentObject += char;
if (!inString) {
if (char === '{' || char === '[') {
depth++;
} else if (char === '}' || char === ']') {
depth--;
if (depth === 0) {
// 完全なオブジェクトを発見
try {
const obj = JSON.parse(currentObject);
onObject(obj);
currentObject = '';
} catch (error) {
onError(error, currentObject);
}
}
}
}
if (char === '"' && !escaped) {
inString = !inString;
}
escaped = char === '\\' && !escaped;
}
}
// ストリーミングパーサーの使用例
const streamData = '{"id":1,"name":"A"}{"id":2,"name":"B"}{"id":3,"name":"C"}';
const objects = [];
parseJsonStream(streamData,
(obj) => objects.push(obj),
(error, content) => console.error('Parse error:', error.message, content)
);
console.log('Streamed objects:', objects);
Web API統合
// Fetch APIとJSONの統合
class ApiClient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
this.defaultHeaders = {
'Content-Type': 'application/json',
'Accept': 'application/json'
};
}
async request(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
const config = {
headers: { ...this.defaultHeaders, ...options.headers },
...options
};
// リクエストボディのJSONシリアライゼーション
if (config.body && typeof config.body === 'object') {
config.body = JSON.stringify(config.body);
}
try {
const response = await fetch(url, config);
// レスポンスヘッダーの確認
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
throw new Error(`Unexpected content type: ${contentType}`);
}
const data = await response.json();
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${data.message || 'Request failed'}`);
}
return data;
} catch (error) {
console.error('API request failed:', error);
throw error;
}
}
async get(endpoint) {
return this.request(endpoint, { method: 'GET' });
}
async post(endpoint, data) {
return this.request(endpoint, {
method: 'POST',
body: data
});
}
async put(endpoint, data) {
return this.request(endpoint, {
method: 'PUT',
body: data
});
}
async delete(endpoint) {
return this.request(endpoint, { method: 'DELETE' });
}
}
// APIクライアントの使用例
const api = new ApiClient('https://api.example.com');
async function manageUsers() {
try {
// ユーザー一覧取得
const users = await api.get('/users');
console.log('Users:', users);
// 新規ユーザー作成
const newUser = {
name: '佐藤花子',
email: '[email protected]',
department: '開発部'
};
const createdUser = await api.post('/users', newUser);
console.log('Created user:', createdUser);
// ユーザー情報更新
const updatedUser = await api.put(`/users/${createdUser.id}`, {
...createdUser,
department: '企画部'
});
console.log('Updated user:', updatedUser);
} catch (error) {
console.error('User management failed:', error);
}
}
// WebSocketとJSONの統合
class JsonWebSocketClient {
constructor(url) {
this.url = url;
this.ws = null;
this.messageHandlers = new Map();
}
connect() {
return new Promise((resolve, reject) => {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('WebSocket connected');
resolve();
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
reject(error);
};
this.ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
this.handleMessage(data);
} catch (error) {
console.error('Failed to parse WebSocket message:', error);
}
};
});
}
send(type, payload) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
const message = {
type,
payload,
timestamp: Date.now()
};
this.ws.send(JSON.stringify(message));
}
}
on(messageType, handler) {
this.messageHandlers.set(messageType, handler);
}
handleMessage(data) {
const handler = this.messageHandlers.get(data.type);
if (handler) {
handler(data.payload);
}
}
disconnect() {
if (this.ws) {
this.ws.close();
}
}
}
// WebSocketクライアントの使用例
const wsClient = new JsonWebSocketClient('wss://api.example.com/ws');
wsClient.on('user_update', (userData) => {
console.log('User updated:', userData);
});
wsClient.on('notification', (notification) => {
console.log('Notification:', notification);
});
// 接続とメッセージ送信
wsClient.connect().then(() => {
wsClient.send('subscribe', { channel: 'user_updates' });
wsClient.send('user_action', { action: 'get_profile', userId: 123 });
});