JavaScript für ungeduldige Programmierer (ES2022-Ausgabe)
Bitte unterstützen Sie dieses Buch: kaufen Sie es oder spenden Sie
(Werbung, bitte nicht blockieren.)

45 Erstellen und Parsen von JSON (JSON)



JSON („JavaScript Object Notation“) ist ein Speicherformat, das Text zur Kodierung von Daten verwendet. Seine Syntax ist eine Untermenge von JavaScript-Ausdrücken. Betrachten Sie als Beispiel den folgenden Text, der in einer Datei jane.json gespeichert ist.

{
  "first": "Jane",
  "last": "Porter",
  "married": true,
  "born": 1890,
  "friends": [ "Tarzan", "Cheeta" ]
}

JavaScript hat das globale Namensraumobjekt JSON, das Methoden zum Erstellen und Parsen von JSON bereitstellt.

45.1 Die Entdeckung und Standardisierung von JSON

Eine Spezifikation für JSON wurde 2001 von Douglas Crockford unter json.org veröffentlicht. Er erklärt:

Ich habe JSON entdeckt. Ich behaupte nicht, JSON erfunden zu haben, denn es existierte bereits in der Natur. Was ich tat, war, es zu finden, ihm einen Namen zu geben und zu beschreiben, wie es nützlich war. Ich behaupte nicht, die erste Person zu sein, die es entdeckt hat; ich weiß, dass es andere Leute gab, die es mindestens ein Jahr vor mir entdeckt haben. Das früheste Vorkommen, das ich gefunden habe, war bei jemandem bei Netscape, der JavaScript-Array-Literale zur Datenkommunikation bereits 1996 verwendete, was mindestens fünf Jahre war, bevor ich auf die Idee stieß.

Später wurde JSON als ECMA-404 standardisiert.

45.1.1 Die Grammatik von JSON ist eingefroren

Zitiert aus dem ECMA-404-Standard:

Da sie so einfach ist, wird nicht erwartet, dass sich die JSON-Grammatik jemals ändern wird. Dies verleiht JSON als grundlegender Notation eine enorme Stabilität.

Daher wird JSON niemals Verbesserungen wie optionale abschließende Kommas, Kommentare oder unzitierte Schlüssel erhalten – unabhängig davon, ob sie als wünschenswert erachtet werden oder nicht. Dies lässt jedoch Raum für die Erstellung von Obermengen von JSON, die zu einfachem JSON kompiliert werden.

45.2 JSON-Syntax

JSON besteht aus den folgenden Teilen von JavaScript:

Infolgedessen können Sie zyklische Strukturen in JSON nicht (direkt) darstellen.

45.3 Verwendung der JSON-API

Das globale Namensraumobjekt JSON enthält Methoden für die Arbeit mit JSON-Daten.

45.3.1 JSON.stringify(data, replacer?, space?)

.stringify() konvertiert JavaScript-data in einen JSON-String. In diesem Abschnitt ignorieren wir den Parameter replacer; er wird in §45.4 „Anpassen von Stringifying und Parsen“ erklärt.

45.3.1.1 Ergebnis: eine einzelne Textzeile

Wenn Sie nur das erste Argument angeben, gibt .stringify() eine einzelne Textzeile zurück.

assert.equal(
  JSON.stringify({foo: ['a', 'b']}),
  '{"foo":["a","b"]}' );
45.3.1.2 Ergebnis: ein Baum aus eingerückten Zeilen

Wenn Sie eine nicht-negative Ganzzahl für space angeben, gibt .stringify() eine oder mehrere Zeilen zurück und rückt mit space Leerzeichen pro Verschachtelungsebene ein.

assert.equal(
JSON.stringify({foo: ['a', 'b']}, null, 2),
`{
  "foo": [
    "a",
    "b"
  ]
}`);
45.3.1.3 Details zum Stringifizieren von JavaScript-Daten

Primitive Werte

Objekte

45.3.2 JSON.parse(text, reviver?)

.parse() konvertiert einen JSON-text in einen JavaScript-Wert. In diesem Abschnitt ignorieren wir den Parameter reviver; er wird in §45.4 „Anpassen von Stringifying und Parsen“ erklärt.

Dies ist ein Beispiel für die Verwendung von .parse().

> JSON.parse('{"foo":["a","b"]}')
{ foo: [ 'a', 'b' ] }

45.3.3 Beispiel: Konvertierung von und nach JSON

Die folgende Klasse implementiert Konvertierungen von (Zeile A) und nach (Zeile B) JSON.

class Point {
  static fromJson(jsonObj) { // (A)
    return new Point(jsonObj.x, jsonObj.y);
  }

  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  
  toJSON() { // (B)
    return {x: this.x, y: this.y};
  }
}

  Übung: Konvertierung eines Objekts von und nach JSON

exercises/json/to_from_json_test.mjs

45.4 Anpassen von Stringifying und Parsen (fortgeschritten)

Stringifying und Parsen können wie folgt angepasst werden:

45.4.1 .stringfy(): Angabe, welche Eigenschaften von Objekten stringifiziert werden sollen

Wenn der zweite Parameter von .stringify() ein Array ist, dann werden nur die Objekteigenschaften, deren Namen dort genannt sind, in das Ergebnis aufgenommen.

const obj = {
  a: 1,
  b: {
    c: 2,
    d: 3,
  }
};
assert.equal(
  JSON.stringify(obj, ['b', 'c']),
  '{"b":{"c":2}}');

45.4.2 .stringify() und .parse(): Wertbesucher

Was ich als Wertbesucher bezeichne, ist eine Funktion, die JavaScript-Daten transformiert.

In diesem Abschnitt werden JavaScript-Daten als Baum von Werten betrachtet. Wenn die Daten atomar sind, ist es ein Baum, der nur einen Wurzelknoten hat. Alle Werte im Baum werden nacheinander an den Wertbesucher übergeben. Je nachdem, was der Besucher zurückgibt, wird der aktuelle Wert weggelassen, geändert oder beibehalten.

Ein Wertbesucher hat die folgende Typsignatur:

type ValueVisitor = (key: string, value: any) => any;

Die Parameter sind:

Der Wertbesucher kann zurückgeben:

45.4.3 Beispiel: Besuchen von Werten

Der folgende Code zeigt, in welcher Reihenfolge ein Wertbesucher Werte sieht.

const log = [];
function valueVisitor(key, value) {
  log.push({this: this, key, value});
  return value; // no change
}

const root = {
  a: 1,
  b: {
    c: 2,
    d: 3,
  }
};
JSON.stringify(root, valueVisitor);
assert.deepEqual(log, [
  { this: { '': root }, key: '',  value: root   },
  { this: root        , key: 'a', value: 1      },
  { this: root        , key: 'b', value: root.b },
  { this: root.b      , key: 'c', value: 2      },
  { this: root.b      , key: 'd', value: 3      },
]);

Wie wir sehen können, besucht der Replacer von JSON.stringify() Werte von oben nach unten (Wurzel zuerst, Blätter zuletzt). Das Motiv für diese Richtung ist, dass wir JavaScript-Werte in JSON-Werte konvertieren. Und ein einzelnes JavaScript-Objekt kann in einen Baum von JSON-kompatiblen Werten erweitert werden.

Im Gegensatz dazu besucht der Reviver von JSON.parse() Werte von unten nach oben (Blätter zuerst, Wurzel zuletzt). Das Motiv für diese Richtung ist, dass wir JSON-Werte zu JavaScript-Werten zusammensetzen. Daher müssen wir die Teile konvertieren, bevor wir das Ganze konvertieren können.

45.4.4 Beispiel: Stringifizieren nicht unterstützter Werte

JSON.stringify() hat keine spezielle Unterstützung für reguläre Ausdrucksobjekte – es stringifiziert sie, als wären sie einfache Objekte.

const obj = {
  name: 'abc',
  regex: /abc/ui,
};
assert.equal(
  JSON.stringify(obj),
  '{"name":"abc","regex":{}}');

Wir können das über einen Replacer beheben:

function replacer(key, value) {
  if (value instanceof RegExp) {
    return {
      __type__: 'RegExp',
      source: value.source,
      flags: value.flags,
    };
  } else {
    return value; // no change
  }
}
assert.equal(
JSON.stringify(obj, replacer, 2),
`{
  "name": "abc",
  "regex": {
    "__type__": "RegExp",
    "source": "abc",
    "flags": "iu"
  }
}`);

45.4.5 Beispiel: Parsen nicht unterstützter Werte

Um das Ergebnis aus dem vorherigen Abschnitt mit JSON.parse() zu parsen, benötigen wir einen Reviver.

function reviver(key, value) {
  // Very simple check
  if (value && value.__type__ === 'RegExp') {
    return new RegExp(value.source, value.flags);
  } else {
    return value;
  }
}
const str = `{
  "name": "abc",
  "regex": {
    "__type__": "RegExp",
    "source": "abc",
    "flags": "iu"
  }
}`;
assert.deepEqual(
  JSON.parse(str, reviver),
  {
    name: 'abc',
    regex: /abc/ui,
  });

45.5 FAQ

45.5.1 Warum unterstützt JSON keine Kommentare?

Douglas Crockford erklärt dies in einem Google+ Post vom 1. Mai 2012.

Ich habe Kommentare aus JSON entfernt, weil ich sah, dass Leute sie für Parsing-Direktiven verwendeten, eine Praxis, die die Interoperabilität zerstört hätte. Ich weiß, dass das Fehlen von Kommentaren einige Leute traurig macht, aber das sollte es nicht.

Angenommen, Sie verwenden JSON, um Konfigurationsdateien zu speichern, die Sie kommentieren möchten. Fügen Sie einfach alle gewünschten Kommentare ein. Leiten Sie sie dann durch JSMin [einen Minifier für JavaScript], bevor Sie sie an Ihren JSON-Parser übergeben.