De `JSON`-klasse in JavaScript

De `JSON`-klasse in JavaScript

Dit artikel legt de JSON-klasse in JavaScript uit.

We leggen de JSON-klasse in JavaScript uit met praktische voorbeelden.

YouTube Video

De JSON-klasse in JavaScript

Het JSON-object heeft voornamelijk twee methoden. JSON.stringify() zet een object om in een JSON-string en JSON.parse() maakt een object van een JSON-string. JSON is een data-uitwisselingsformaat dat slechts sommige JavaScript-waarden kan weergeven. Functies, undefined, Symbol en circulaire verwijzingen kunnen niet direct worden omgezet naar JSON.

JSON.stringify()

Hier is eerst een basisvoorbeeld van het omzetten van een object naar een JSON-string.

1// Convert a JavaScript object to a JSON string.
2const obj = { name: "Alice", age: 30, active: true };
3const jsonString = JSON.stringify(obj);
4console.log(jsonString); // {"name":"Alice","age":30,"active":true}
  • Deze code is het meest eenvoudige voorbeeld van het omzetten van een gewoon object naar een JSON-string. JSON.stringify geeft de string synchroon terug.

Het tweede argument van JSON.stringify (replacer)

Door replacer, het tweede argument van JSON.stringify, op te geven kun je de conversieregels nauwkeurig bepalen. Hier is een voorbeeld waarbij een array wordt gebruikt om alleen bepaalde eigenschappen te behouden.

1// Use a replacer array to include only specific properties during stringification.
2const user = { id: 1, name: "Bob", password: "secret", role: "admin" };
3const safeJson = JSON.stringify(user, ["id", "name", "role"]);
4console.log(safeJson); // {"id":1,"name":"Bob","role":"admin"}
  • In dit voorbeeld wordt password uitgesloten zodat veilige loguitvoer mogelijk is. Dit is handig als je om veiligheidsredenen gevoelige informatie wilt verwijderen.

Replacer als een functie gebruiken (voor waardetransformatie en filtering)

Als je meer flexibiliteit wilt, kun je een functie opgeven om elke sleutel en waarde te verwerken. Hieronder staat een voorbeeld waarbij een Date als een ISO-string wordt behouden.

 1// Use a replacer function to customize serialization.
 2const data = { name: "Carol", joined: new Date("2020-01-01T12:00:00Z") };
 3const jsonCustom = JSON.stringify(data, (key, value) => {
 4  // Convert Date objects to ISO strings explicitly
 5  if (this && this[key] instanceof Date) {
 6    return this[key].toISOString();
 7  }
 8  return value;
 9});
10console.log(jsonCustom); // {"name":"Carol","joined":"2020-01-01T12:00:00.000Z"}
  • Bij een functionele replacer worden de sleutel en waarde doorgegeven en wordt de geretourneerde waarde in de uiteindelijke JSON geplaatst. this verwijst naar het bovenliggende object, dus het kan ook voor geneste conversies worden gebruikt.

De basis van JSON.parse()

JSON.parse zet een JSON-string terug om in een object. Omring het met try/catch zodat je ongeldige JSON kunt afhandelen.

1// Parse a JSON string into an object and handle parsing errors.
2const jsonText = '{"title":"Example","count":42}';
3try {
4  const parsed = JSON.parse(jsonText);
5  console.log(parsed.title); // Example
6} catch (err) {
7  console.error("Invalid JSON:", err.message);
8}
  • Als de string ongeldig is, wordt er een uitzondering opgegooid, dus zorg altijd voor foutafhandeling als je met externe bronnen werkt.

Aangepaste restauratie met reviver (voorbeeld van het herstellen van een Date-object)

Door het tweede argument reviver van JSON.parse te gebruiken kun je waarden herstellen. Hier is een voorbeeld waarbij een opgeslagen stringdatum weer wordt omgezet naar een Date-object.

 1// Use a reviver to turn ISO date strings back into Date objects.
 2const jsonWithDate = '{"name":"Dana","joined":"2021-06-15T09:00:00.000Z"}';
 3const obj = JSON.parse(jsonWithDate, (key, value) => {
 4  // A simple check for ISO date-like strings
 5  if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(value)) {
 6    return new Date(value);
 7  }
 8  return value;
 9});
10console.log(obj.joined instanceof Date); // true
  • reviver wordt voor elke sleutel aangeroepen, en de geretourneerde waarde wordt gebruikt in het uiteindelijke object. Voer strikte controle uit op het datumformaat, afhankelijk van je toepassing.

Mooi afdrukken en het space-argument

Om met inspringing voor de leesbaarheid uit te voeren, geef je een getal of string op als derde argument aan JSON.stringify.

1// Pretty-print an object with 2-space indentation for readability.
2const config = { host: "localhost", port: 3000, debug: true };
3const pretty = JSON.stringify(config, null, 2);
4console.log(pretty);
5/* {
6  "host": "localhost",
7  "port": 3000,
8  "debug": true
9} */
  • Voor JSON die door mensen moet worden gelezen, zoals logboeken of configuratiebestanden, is het handig om inspringing te gebruiken. Let er echter op dat dit de bestandsgrootte vergroot.

Hoe ga je om met circulaire verwijzingen

JSON.stringify geeft een TypeError als er een circulaire verwijzing is. Een veelgebruikte oplossing is een replacer maken die een seen-set gebruikt om circulaire verwijzingen te vermijden.

 1// Safely stringify objects that may contain circular references.
 2function safeStringify(value) {
 3  const seen = new WeakSet();
 4  return JSON.stringify(value, (key, val) => {
 5    if (val && typeof val === "object") {
 6      if (seen.has(val)) return "[Circular]";
 7      seen.add(val);
 8    }
 9    return val;
10  });
11}
12
13const a = { name: "A" };
14a.self = a;
15console.log(safeStringify(a)); // {"name":"A","self":"[Circular]"}
  • Door een WeakSet te gebruiken kun je circulaire verwijzingen detecteren zonder geheugenslekken te veroorzaken. Bij een circulaire verwijzing retourneert hij "[Circular]". Het is ook mogelijk om in plaats van "[Circular]" een referentie-ID toe te wijzen.

Aangepaste serialisatie met de toJSON-methode

Als je een toJSON-methode op een object definieert, gebruikt JSON.stringify de geretourneerde waarde daarvan. Dit is handig als je conversieregels per type wilt inbedden.

 1// Define toJSON on a class to customize its JSON representation.
 2class Point {
 3  constructor(x, y) {
 4    this.x = x;
 5    this.y = y;
 6  }
 7  toJSON() {
 8    // This will be used by JSON.stringify
 9    return { x: this.x, y: this.y, type: "Point" };
10  }
11}
12
13const p = new Point(10, 20);
14console.log(JSON.stringify(p)); // {"x":10,"y":20,"type":"Point"}
  • toJSON stelt je in staat om serialisatieregels op objectniveau te definiëren, wat lokaler en intuïtiever is dan een replacer gebruiken.

Beperkingen en aandachtspunten van JSON

Belangrijke voorbeelden die niet naar JSON kunnen worden geconverteerd zijn undefined, functies, Symbol, BigInt en circulaire verwijzingen. Let ook op numerieke precisie (zoals grote gehele getallen en afronding van IEEE-kommagetallen).

 1// Demonstrate values that can't be represented in JSON.
 2const sample = {
 3  a: undefined,
 4  b: function () {},
 5  c: Symbol("s"),
 6  d: 9007199254740993n // BigInt (note: JSON.stringify will throw on BigInt)
 7};
 8// JSON.stringify will throw for BigInt and will drop undefined, functions, symbols in objects.
 9try {
10  console.log(JSON.stringify(sample));
11} catch (err) {
12  console.error("Error during stringify:", err.message);
13}
  • Bij het omgaan met BigInt kun je het omzetten naar een string of de representatie bepalen met een aangepaste replacer of toJSON.

Beveiliging (bij het verwerken van onbetrouwbare JSON)

JSON.parse is zelf veilig en wordt veiliger geacht dan eval, maar het is gevaarlijk om het geparse object te vertrouwen en direct te gebruiken voor property-toegang of database-queries. Valideer altijd en controleer of de data voldoet aan het verwachte schema.

1// Parse external JSON and validate expected properties before use.
2const external = '{"username":"eve","role":"user"}';
3const parsed = JSON.parse(external);
4if (typeof parsed.username === "string" && ["user","admin"].includes(parsed.role)) {
5  console.log("Safe to use parsed.username and parsed.role.");
6} else {
7  throw new Error("Invalid payload");
8}
  • Voor invoervalidatie (schema-validatie) zijn libraries zoals ajv robuust.

Prestaties overwegen

Veelvuldig serialiseren en deserialiseren van grote objecten belast de CPU en het geheugen. Indien nodig kun je overwegen alleen de verschillen te sturen, binaire formaten te gebruiken (zoals MessagePack) of te streamen (gegevens sequentieel verwerken). In browseromgevingen kun je soms structuredClone gebruiken (voor kopiëren) of overdraagbare objecten in postMessage.

Specifiek advies (kort)

Je kunt ook de volgende punten overwegen:.

  • Vermijd voor logs pretty-print en gebruik JSON op één regel om de grootte te beperken.
  • Objecten die vaak worden geserialiseerd, moeten van tevoren lichtgewicht worden gemaakt, bijvoorbeeld door onnodige eigenschappen te verwijderen.
  • Gebruik replacer om traversals te minimaliseren en onnodige eigenschappen uit te sluiten.

Praktisch voorbeeld: API-verzoek verzend-/ontvangststroom

Tot slot is hier een reeks stappen die laat zien hoe je veilig een object naar JSON converteert om naar een server te sturen en een datum herstelt uit het ontvangen antwoord.

 1// Prepare payload, stringify safely, send via fetch, and revive dates on response.
 2async function sendUserUpdate(url, user) {
 3  // Remove sensitive info before sending
 4  const payload = JSON.stringify(user, (k, v) => (k === "password" ? undefined : v));
 5  const res = await fetch(url, {
 6    method: "POST",
 7    headers: { "Content-Type": "application/json" },
 8    body: payload
 9  });
10
11  const text = await res.text();
12  // Reviver: convert ISO date strings back to Date
13  const data = JSON.parse(text, (key, value) => {
14    if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(value)) {
15      return new Date(value);
16    }
17    return value;
18  });
19  return data;
20}
  • In dit voorbeeld wordt password uitgesloten voor verzending en wordt de datum na ontvangst door reviver hersteld. In de praktijk moet je foutafhandeling en time-outverwerking toevoegen.

Samenvatting

Met JSON.stringify() en JSON.parse() kun je tussen objecten en strings omzetten. Je kunt het conversieproces aanpassen met replacer en reviver, en als je toJSON in een klasse implementeert, kun je ook elk object afzonderlijk beheren. Circulaire verwijzingen, BigInt, functies, enz. kunnen niet direct worden verwerkt, dus je moet ze vooraf behandelen.

Valideer altijd externe invoer en zorg ervoor dat gevoelige informatie niet wordt opgenomen in logs of communicatie. Als de hoeveelheid data groot is, is het efficiënt om pretty-printing te vermijden, differentiële overdracht te overwegen of binaire formaten te gebruiken.

Je kunt het bovenstaande artikel volgen met Visual Studio Code op ons YouTube-kanaal. Bekijk ook het YouTube-kanaal.

YouTube Video