% |
| Custom Replacer/Reviver | 100% | 15.7 | 0% |
Key Findings:
structuredClone() outperforms naive JSON cloning by ~35% in execution time while preserving 99% of built-in types (Date, Map, Set, ArrayBuffer, etc.).
- Custom replacer/reviver pipelines introduce ~20% overhead but guarantee 100% type fidelity and explicit error boundaries.
- Sweet Spot: Use
structuredClone() for general-purpose deep cloning. Reserve custom replacer/reviver architectures for cross-boundary serialization (APIs, storage, workers) where type contracts must be explicitly defined and validated.
Core Solution
1. Safe Parsing & Validation Architecture
Centralize JSON parsing to return structured results instead of throwing unhandled exceptions.
function isValidJSON(str) {
try {
JSON.parse(str);
return true;
} catch {
return false;
}
}
// Versión que devuelve el error
function parseJSON(str) {
try {
return { ok: true, data: JSON.parse(str) };
} catch (err) {
return { ok: false, error: err.message };
}
}
const result = parseJSON('{"broken": json}');
result.ok; // false
result.error; // 'Unexpected token j in JSON at position 12'
2. Handling Non-Serializable Types
Native serialization drops or mutates complex types. Use a replacer/reviver pair to explicitly map them.
JSON.stringify({
fn: () => {}, // undefined (se omite)
sym: Symbol('x'), // undefined (se omite)
undef: undefined, // undefined (se omite)
nan: NaN, // null
inf: Infinity, // null
date: new Date(), // string ISO
regexp: /regex/, // {} ← TRAMPA
map: new Map([['a', 1]]), // {} ← TRAMPA
set: new Set([1, 2]), // [] ← TRAMPA
});
Para serializar Map y Set:
function replacer(key, value) {
if (value instanceof Map) {
return { __type: 'Map', entries: [...value.entries()] };
}
if (value instanceof Set) {
return { __type: 'Set', values: [...value] };
}
return value;
}
function reviver(key, value) {
if (value?.__type === 'Map') return new Map(value.entries);
if (value?.__type === 'Set') return new Set(value.values);
return value;
}
const data = { myMap: new Map([['a', 1]]) };
const json = JSON.stringify(data, replacer, 2);
const back = JSON.parse(json, reviver);
back.myMap instanceof Map; // true
3. Field Filtering & Sanitization
Leverage the replacer parameter for whitelisting or redacting sensitive fields.
// Solo incluir campos específicos
JSON.stringify(user, ['name', 'email'], 2);
// Excluir campos sensibles
function omitSensitive(key, value) {
const sensitive = ['password', 'token', 'secret'];
if (sensitive.includes(key)) return undefined;
return value;
}
JSON.stringify(user, omitSensitive, 2);
4. Modern Cloning Strategy
Replace legacy JSON cloning with engine-native deep cloning.
// Forma rápida (limitada — pierde funciones, fechas, etc.)
const clone = JSON.parse(JSON.stringify(obj));
// Forma moderna y correcta
const clone = structuredClone(obj); // soporta Date, Map, Set, etc.
structuredClone() is available in Node 17+ and all modern browsers.
5. Configuration vs API Standards
JSON5 extends JSON with developer-friendly syntax but must be isolated from strict API contracts.
{
// Comentarios
name: 'sin comillas en keys',
trailing: 'coma',
hex: 0xFF,
multiline: "primera línea \
segunda línea",
}
JSON5 is useful for configuration files but not for APIs (always use standard JSON in APIs).
Pitfall Guide
- Silent Type Omission:
undefined, functions, and symbols are stripped from objects without warnings. Always audit payloads for missing keys before serialization.
- Numeric Edge Cases:
NaN and Infinity coerce to null. Mathematical or financial payloads will silently corrupt if these values are not explicitly handled or validated pre-serialization.
- Collection Serialization Traps:
RegExp, Map, and Set serialize to {} or [], losing methods and internal state. Never assume structural preservation; always implement explicit replacer/reviver mappings.
- Naive Deep Cloning:
JSON.parse(JSON.stringify()) destroys prototypes, discards Dates/Maps/Sets, and throws on circular references. Migrate to structuredClone() for in-memory duplication.
- Scattered Error Handling: Inline
try/catch blocks fragment error state. Centralize parsing behind a Result pattern ({ ok, data, error }) to enable consistent upstream error routing and logging.
- JSON5 in Production APIs: JSON5 syntax (comments, trailing commas, unquoted keys) fails strict JSON parsers in distributed systems. Isolate JSON5 to local config loaders; enforce RFC 8259 compliance for all network boundaries.
- Replacer Context Loss: The replacer function receives the root object with an empty string key (
""). Failing to account for this can cause unexpected filtering or type coercion at the payload root.
Deliverables
- Blueprint: JSON Serialization & Validation Architecture — A layered strategy combining centralized parsing, explicit type mapping, and engine-native cloning. Includes decision trees for choosing between
structuredClone(), custom replacers, and strict validation gates.
- Checklist: Pre-flight JSON Validation & Serialization Audit
- Configuration Templates:
safe-parse.ts — Centralized parsing utility with structured error returns
type-replacer.js — Reusable replacer/reviver factory for Map/Set/RegExp
field-filter.config.json — Whitelist/blacklist mapping for sensitive data redaction