Lewati ke konten
Toova
Semua Alat

7 Trik JSON yang Akan Menghemat Berjam-jam Waktu Anda

Toova

Setiap developer JavaScript menggunakan JSON.parse dan JSON.stringify puluhan kali seminggu. Tetapi sebagian besar berhenti pada dasar-dasarnya — mem-parsing respons API dan menserialisasi objek ke string. API ini punya lebih banyak penawaran, dan beberapa fitur yang jarang digunakan memecahkan masalah yang sebaliknya membutuhkan library pihak ketiga atau berjam-jam debugging.

Panduan ini membahas tujuh trik yang melampaui default. Masing-masing dapat segera diterapkan ke basis kode nyata. Tanpa abstraksi, tanpa contoh karangan — ini adalah pola yang muncul di sistem produksi.

Anda dapat memverifikasi dan menjelajahi contoh kode mana pun di artikel ini menggunakan Toova JSON Formatter, yang memvalidasi dan mencetak JSON dengan rapi sepenuhnya di peramban Anda.

1. Deep Clone dengan Round-Trip JSON (dan Batasannya)

Trik tertua dalam playbook JavaScript: gunakan JSON.parse(JSON.stringify(obj)) untuk membuat deep clone dari objek biasa.

const original = { a: 1, b: { c: 2 } };

// Pendekatan naif — objek bersarang dalam masih dibagikan
const shallowCopy = { ...original }; // b masih menunjuk ke objek yang sama

// JSON deep clone — membuat salinan yang sepenuhnya independen
const deepClone = JSON.parse(JSON.stringify(original));

deepClone.b.c = 99;
console.log(original.b.c); // 2 — asli tidak tersentuh

Ini bekerja karena menserialisasi ke string dan mem-parsing kembali menciptakan pohon objek yang sepenuhnya baru tanpa referensi bersama. Cepat, tidak memerlukan dependensi, dan telah tersedia sejak ES5.

Alternatif modern untuk penggunaan in-memory adalah structuredClone():

// Alternatif modern: structuredClone() — menangani lebih banyak tipe
// Didukung di Node.js 17+ dan semua peramban evergreen
const clone = structuredClone(original);

Ketahui batasan pendekatan JSON sebelum mengandalkannya:

// JSON.parse/stringify TIDAK BISA menangani:
const broken = {
  date: new Date(),      // menjadi string — kehilangan prototype Date
  fn: () => 'hello',    // dihapus diam-diam
  undef: undefined,     // dihapus diam-diam
  inf: Infinity,        // menjadi null
  map: new Map(),       // menjadi {}
  cycle: null,          // referensi sirkuler memunculkan error
};

Jika objek Anda hanya berisi data biasa — string, angka, boolean, array, dan objek biasa bersarang — round-trip JSON aman dan cepat. Untuk yang lain, lebih baik structuredClone() atau library khusus.

2. Replacer Kustom untuk Filtering dan Masking

Sebagian besar developer tahu bahwa JSON.stringify menerima argumen kedua, tetapi jarang menggunakannya. Argumen kedua itu adalah replacer: bisa berupa array key untuk disertakan, atau fungsi yang mengontrol persis bagaimana setiap nilai diserialisasi.

Replacer array — whitelist key tertentu:

const user = {
  id: 'u_001',
  name: 'Alice',
  password: 'hunter2',        // tidak boleh muncul di log
  creditCard: '4111111111111111', // tidak boleh muncul di log
  role: 'admin',
};

// Replacer sebagai array: hanya sertakan key ini
JSON.stringify(user, ['id', 'name', 'role']);
// '{"id":"u_001","name":"Alice","role":"admin"}'

Replacer fungsi — transformasi atau redaksi nilai:

// Replacer sebagai fungsi: kontrol penuh atas key/value
const masked = JSON.stringify(user, (key, value) => {
  if (key === 'password' || key === 'creditCard') return '[REDACTED]';
  if (key === '' ) return value; // objek root — selalu kembalikan
  return value;
});
// '{"id":"u_001","name":"Alice","password":"[REDACTED]","creditCard":"[REDACTED]","role":"admin"}'

Versi yang lebih canggih melakukan masking nilai berdasarkan bentuknya daripada nama key-nya:

// Replacer untuk masking berbasis tipe
const sanitize = (key, value) => {
  if (typeof value === 'string' && value.match(/^4[0-9]{15}$/)) {
    return '****-****-****-' + value.slice(-4);
  }
  return value;
};

Teknik ini sangat diperlukan untuk middleware logging: Anda menginginkan log terstruktur dengan konteks objek penuh, tetapi field tertentu tidak boleh mencapai agregator log. Replacer memungkinkan Anda menangani ini di batas serialisasi alih-alih menyebarkan logika redaksi di seluruh basis kode.

3. Urutkan Key Secara Deterministik

Urutan key objek di JavaScript adalah urutan penyisipan (untuk key string). Dua objek dengan key yang sama tetapi dibuat dalam urutan berbeda menghasilkan string JSON berbeda, yang merusak pemeriksaan kesetaraan naif, kunci cache, dan hash konten.

function sortedStringify(obj) {
  return JSON.stringify(obj, Object.keys(obj).sort());
}

const a = { z: 1, a: 2, m: 3 };
const b = { a: 2, m: 3, z: 1 };

sortedStringify(a) === sortedStringify(b); // true — urutan key dinormalisasi

Untuk objek bersarang dalam, terapkan pengurutan secara rekursif:

// Pengurutan key rekursif untuk objek bersarang
function sortKeys(value) {
  if (Array.isArray(value)) return value.map(sortKeys);
  if (value !== null && typeof value === 'object') {
    return Object.fromEntries(
      Object.keys(value).sort().map((k) => [k, sortKeys(value[k])])
    );
  }
  return value;
}

const sorted = JSON.stringify(sortKeys(deepNested));

JSON terurut sangat penting saat Anda:

  • Menghasilkan kunci cache dari body request
  • Menghitung checksum atau tanda tangan atas payload JSON
  • Membandingkan respons API dalam pengujian terlepas dari urutan field
  • Menyimpan objek konfigurasi di mana urutan seharusnya tidak memengaruhi kesetaraan

Setelah pengurutan dan pretty-printing, gunakan alat Text Diff untuk membandingkan dua string JSON yang dinormalisasi dan melihat persis nilai mana yang berubah antar versi.

4. Menangani BigInt Tanpa Kehilangan Presisi

Tipe Number JavaScript dapat dengan aman merepresentasikan integer hingga 253 − 1. Untuk ID yang dihasilkan oleh sistem terdistribusi, jumlah keuangan dalam satuan minor, atau timestamp dalam nanodetik, ini tidak cukup. BigInt mencakup integer presisi sembarang, tetapi JSON.stringify tidak tahu apa yang harus dilakukan dengannya.

const data = {
  amount: 9007199254740993n, // lebih besar dari Number.MAX_SAFE_INTEGER
};

// Ini memunculkan: TypeError: Do not know how to serialize a BigInt
JSON.stringify(data); // ERROR

Solusi standar:

// Solusi 1: Konversi ke string dengan replacer
JSON.stringify(data, (key, value) =>
  typeof value === 'bigint' ? value.toString() : value
);
// '{"amount":"9007199254740993"}'

// Solusi 2: toJSON() pada prototype BigInt (monkey-patch — gunakan dengan hati-hati)
BigInt.prototype.toJSON = function () { return this.toString(); };
JSON.stringify(data); // '{"amount":"9007199254740993"}'

Di sisi parsing, fungsi reviver dapat memulihkan nilai BigInt dari representasi string-nya:

// Reviver untuk memulihkan BigInt saat parse
const revived = JSON.parse('{"amount":"9007199254740993"}', (key, value) => {
  if (key === 'amount') return BigInt(value);
  return value;
});
console.log(typeof revived.amount); // 'bigint'

Pertahankan konversi BigInt-ke-string dalam lapisan serialisasi bersama sehingga ia diterapkan secara konsisten. Membiarkan BigInt bocor ke panggilan JSON.stringify ad hoc di tepi basis kode menyebabkan kesalahan yang tidak dapat diprediksi yang sulit dilacak.

5. Deteksi Referensi Sirkuler

Referensi sirkuler terjadi saat objek berisi referensi ke dirinya sendiri atau ke leluhur dalam graf objek. Mereka lebih umum daripada yang mungkin Anda kira: event emitter, node DOM, node fiber React, dan entitas ORM semuanya sering memiliki referensi balik.

// Contoh referensi sirkuler
const obj = { name: 'node' };
obj.self = obj; // obj mereferensikan dirinya sendiri

JSON.stringify(obj); // memunculkan: TypeError: Converting circular structure to JSON

Tangani dengan replacer kustom yang melacak objek yang telah dikunjungi:

// Penangan referensi sirkuler manual
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;
  });
}

safeStringify(obj); // '{"name":"node","self":"[Circular]"}'

WeakSet adalah struktur data yang tepat di sini: ia menyimpan referensi objek tanpa mencegah pengumpulan sampah, dan pencariannya O(1). Pola ini juga bekerja di middleware logging di mana Anda ingin error terdegradasi dengan anggun alih-alih melempar selama serialisasi.

Variasi umum: alih-alih menandai referensi sirkuler sebagai [Circular], ganti dengan string path seperti [Circular: $.config.parent] untuk membuat lokasi referensi eksplisit selama debugging.

6. Pretty-Print Array di Satu Baris

Argumen ketiga JSON.stringify adalah indent. Mengirim 2 atau 4 memperluas semuanya ke beberapa baris, yang bagus untuk objek tetapi bisa bertele-tele untuk array primitif seperti tag, ID, atau koordinat.

const mixed = {
  title: 'Report',
  tags: ['json', 'api', 'debug'],
  config: { indent: 2, sortKeys: true },
  count: 42,
};

// Default: semuanya multi-baris
JSON.stringify(mixed, null, 2);
// {
//   "title": "Report",
//   "tags": [
//     "json",
//     "api",
//     "debug"
//   ],
//   "config": {
//     "indent": 2,
//     "sortKeys": true
//   },
//   "count": 42
// }

Anda dapat pasca-proses output untuk mengciutkan array sederhana ke satu baris:

// Replacer kustom untuk array satu baris
function prettyMixed(obj) {
  const raw = JSON.stringify(obj, null, 2);
  // Ciutkan array yang hanya berisi primitif ke satu baris
  return raw.replace(
    /\[\n\s+([\s\S]*?)\n\s+\]/g,
    (match, inner) => {
      const items = inner.split(',\n').map((s) => s.trim());
      if (items.every((s) => !/^[{\[]/.test(s))) {
        return '[' + items.join(', ') + ']';
      }
      return match;
    }
  );
}

Hasilnya: objek tetap multi-baris untuk keterbacaan, array primitif menciut ke satu baris untuk keringkasan. Ini adalah format yang digunakan di banyak file konfigurasi dan output log terstruktur di mana manusia dan mesin keduanya perlu membaca data yang sama. JSON Formatter dan konverter JSON ke YAML menangani pengciutan ini secara otomatis.

7. Streaming JSON Besar

Memuat file JSON besar sepenuhnya ke memori sebelum parsing adalah kesalahan kinerja paling umum dalam pipeline pemrosesan JSON. Respons JSON 100 MB mengalokasikan setidaknya 100 MB heap untuk string mentah, lalu alokasi kedua untuk objek yang di-parsing. Untuk file di atas beberapa megabita, parser streaming memproses data secara bertahap.

Di Node.js dengan library streaming:

// node --experimental-vm-modules (atau gunakan library JSON streaming)
// Streaming native dengan proposal JSON Source Map (Stage 3, 2026):

// Untuk saat ini, gunakan library seperti @streamparser/json atau jsonstream2
import { createReadStream } from 'fs';
import parser from '@streamparser/json';

const jsonParser = new parser.JSONParser();
jsonParser.onValue = ({ value, key, parent }) => {
  if (key === 'id') {
    console.log('Found id:', value);
  }
};

createReadStream('large.json').pipe(jsonParser);

Di peramban menggunakan Fetch Streams API:

// Streaming sisi peramban dengan Fetch + JSON stream decoder:
const response = await fetch('/api/large-data');
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });
  // Proses objek JSON lengkap dari buffer...
}

Streaming paling berguna dalam pipeline pemrosesan batch, endpoint ekspor besar, dan skrip analisis log. Untuk respons API biasa di bawah 1 MB, JSON.parse standar cukup cepat dan jauh lebih sederhana. Ambang batas di mana streaming mulai membayar dalam praktik adalah sekitar 5–10 MB, tergantung pada kompleksitas struktur yang di-parsing dan ukuran heap lingkungan target.

Untuk menjelajahi struktur respons JSON besar sebelum Anda membangun parser streaming, tempel sampel ke JSON Formatter untuk melihat jalur mana yang berisi data yang Anda butuhkan, lalu konversikan ke CSV atau YAML untuk analisis lebih lanjut.

Menyatukan Semuanya

Tujuh teknik ini mencakup sudut paling berdampak dari API JSON:

  • Deep clone melalui round-trip — cepat untuk data biasa, ketahui batasannya
  • Replacer — saring key dan masking nilai sensitif di batas serialisasi
  • Pengurutan key — output deterministik untuk kunci cache, tanda tangan, dan assertion uji
  • Penanganan BigInt — stringify ke string, hidupkan kembali saat parse
  • Deteksi referensi sirkuler — replacer berbasis WeakSet untuk serialisasi aman
  • Pretty-print dengan array ringkas — output yang dapat dibaca manusia tanpa whitespace berlebihan
  • Streaming JSON besar — parsing bertahap untuk file besar dan respons API

Untuk dokumentasi lengkap replacer dan argumen space JSON.stringify, lihat referensi JSON.stringify MDN. Untuk alternatif deep clone modern, lihat dokumentasi structuredClone(), yang mencakup semua tipe yang didukung dan edge case.