====== std.json ====== JSON auf Textebene: Sonderzeichen maskieren, Wohlgeformtheit grob prüfen, einen Einzelwert einlesen und **flache Arrays** über ein Pipe-getrenntes Zwischenformat verarbeiten. Ein Dokumentmodell mit Objektzugriff gibt es nicht. → [[lyx_-_programmiersprache:units:string|std.string]] · [[lyx_-_programmiersprache:guides:daten-serialisierung|Guide: Daten & Serialisierung]] · [[lyx_-_programmiersprache:units|Standard Library]] Alle Beispiele dieser Seite sind mit ''lyxc 1.0.21A'' übersetzt und ausgeführt; die gezeigten Ausgaben sind echte Programmausgaben. **Einsatz:** einfache REST-Antworten auswerten, Zahlenlisten übergeben, Werte für die Ausgabe maskieren. > **Was diese Unit nicht kann — vor dem ersten Einsatz lesen:** > > * **Kein Objektzugriff.** „Hole ''user.name'' aus diesem JSON" ist mit ''std.json'' nicht möglich; ''parseValue'' meldet bei einem Objekt nur den Typ und lässt den Zielpuffer unberührt ([[https://github.com/SEOLizer/LyX-Compiler/issues/1435|#1435]]). > * **Kein verlustfreier Durchlauf.** ''toArray'' → ''stringify'' macht aus ''%%[1,2,3]%%'' das Dokument ''%%["1","2","3"]%%'' — aus Zahlen werden Zeichenketten ([[https://github.com/SEOLizer/LyX-Compiler/issues/1432|#1432]]). > * **Keine Prüfung des Inhalts** von Arrays und Objekten: ''%%[abc]%%'' gilt als gültig ([[https://github.com/SEOLizer/LyX-Compiler/issues/1434|#1434]]). > * **Keine Steuerzeichen-Maskierung** — das Ergebnis von ''JSONEscape'' kann ungültiges JSON sein ([[https://github.com/SEOLizer/LyX-Compiler/issues/1433|#1433]]). > > Für echte Dokumentverarbeitung führt der [[lyx_-_programmiersprache:guides:daten-serialisierung|Guide]] die gangbaren Wege auf. ---- ===== Typ- und Fehlercodes ===== ^ Konstante ^ Wert ^ ^ Konstante ^ Wert ^ | ''JSON_NULL'' | 0 | | ''ERR_JSON_OK'' | 0 | | ''JSON_BOOL'' | 1 | | ''ERR_JSON_INVALID'' | 1 | | ''JSON_NUMBER'' | 2 | | ''ERR_JSON_EXPECTED'' | 2 | | ''JSON_STRING'' | 3 | | ''ERR_JSON_EOF'' | 3 | | ''JSON_ARRAY'' | 4 | | ''ERR_JSON_ESCAPE'' | 4 | | ''JSON_OBJECT'' | 5 | | | | Die ''JSON_*''-Werte liefert ''parseValue'' als **Typ**, die ''ERR_JSON_*''-Werte liefert ''parseArray'' als **Fehlercode**. Von den fünf Fehlercodes verwendet die Unit nur ''ERR_JSON_OK'' und ''ERR_JSON_INVALID''. > **Rückgabewerte bedeuten je Funktion etwas anderes.** ''parseArray'' gibt einen **Fehlercode** zurück (''0'' = Erfolg), ''parseValue'' einen **Typ**, ''toArray''/''stringify''/''JSONEscape'' den **Zielzeiger**. Das Ergebnis steht in allen Fällen im Zielpuffer, den der Aufrufer stellt. ---- ===== Prüfen, einlesen, maskieren ===== ^ Signatur ^ Beschreibung ^ | ''isValidJSON(s: pchar): bool'' | Grobprüfung — bei Skalaren genau, bei ''%%[…]%%'' und ''%%{…}%%'' nur die Klammerpaare | | ''parseValue(dest, src): int64'' | Einzelwert; Rückgabe ist der ''JSON_*''-Typ, der Wert landet in ''dest'' | | ''parseArray(dest, src): int64'' | JSON-Array ins Pipe-Format; Rückgabe ist ein ''ERR_JSON_*''-Code | | ''toArray(dest, json_str): pchar'' | wie ''parseArray'', leert ''dest'' im Fehlerfall | | ''JSONEscape(dest, src): pchar'' | maskiert ''"'', ''\'', Zeilenumbruch, Wagenrücklauf, Tabulator — **und setzt Anführungszeichen** | | ''serializeValue(dest, value, json_type): pchar'' | maskiert bei ''JSON_STRING'', kopiert sonst unverändert | | ''serializeArray(dest, src): pchar'' · ''stringify(dest, pipe_str): pchar'' | Pipe-Format zurück nach JSON (identisch) | import std.json; import std.alloc; import std.string; fn J(b: bool): pchar { if (b) { return "gueltig"; } return "ungueltig"; } fn main(): int64 { var d: pchar := alloc(1024) as pchar; // Pruefen: Skalare werden genau geprueft, Arrays und Objekte nur auf Klammerpaare PrintLn(StrConcat("'{\"a\":1}' ", J(isValidJSON("{\"a\":1}"c)))); PrintLn(StrConcat("'[1,2,3]' ", J(isValidJSON("[1,2,3]"c)))); PrintLn(StrConcat("'nonsense' ", J(isValidJSON("nonsense"c)))); PrintLn(StrConcat("'12.5' ", J(isValidJSON("12.5"c)))); PrintLn(StrConcat("'12.5.7' ", J(isValidJSON("12.5.7"c)))); PrintLn(StrConcat("'[abc]' ", J(isValidJSON("[abc]"c)))); // Array einlesen: der Rueckgabewert ist ein Fehlercode, das Ergebnis steht in dest var rc: int64 := parseArray(d, "[1,2,3]"c); PrintLn(StrConcat(StrConcat("parseArray rc=", IntToStr(rc)), StrConcat(" Pipe-Form: ", d))); // Einzelwert einlesen: der Rueckgabewert ist der Typ var typ: int64 := parseValue(d, "\"Hallo\""c); PrintLn(StrConcat(StrConcat("parseValue Typ=", IntToStr(typ)), StrConcat(" Inhalt: ", d))); // Maskieren fuer die Ausgabe JSONEscape(d, "Er sagte \"Hallo\"\nund ging"c); PrintLn(StrConcat("JSONEscape: ", d)); return 0; } '{"a":1}' gueltig '[1,2,3]' gueltig 'nonsense' ungueltig '12.5' gueltig '12.5.7' ungueltig '[abc]' gueltig parseArray rc=0 Pipe-Form: 1|2|3 parseValue Typ=3 Inhalt: Hallo JSONEscape: "Er sagte \"Hallo\"\nund ging" Die Skalarprüfung ist seit #1269 belastbar: ''nonsense'' und ''12.5.7'' werden abgewiesen, ''true''/''false''/''null'' namentlich verglichen. Nur der **Inhalt** von Arrays und Objekten bleibt ungeprüft — dort zählt allein, ob sich zur öffnenden Klammer eine schließende findet. > **''JSONEscape'' maskiert keine Steuerzeichen.** Alles unter U+0020 außer Zeilenumbruch, Wagenrücklauf und Tabulator geht roh durch; RFC 8259 verlangt für jedes dieser Zeichen eine ''\u00XX''-Folge. Ein Byte 0x01 aus einer Protokollzeile oder einem Datenbankfeld macht das Ergebnis für strenge Parser unlesbar ([[https://github.com/SEOLizer/LyX-Compiler/issues/1433|#1433]]). > > Umgekehrt versteht die Unit ''\uXXXX'' beim Einlesen nicht — aus einem maskierten ''A'' wird ''u0041''. ---- ===== Das Pipe-Format ===== Arrays werden nicht als Struktur geführt, sondern als eine Zeichenkette, in der die Elemente durch ''|'' getrennt sind. ''parseArray'' erzeugt sie, ''stringify'' macht daraus wieder JSON. import std.json; import std.alloc; import std.string; fn Zeig(json: pchar): void { var pipe: pchar := alloc(1024) as pchar; var back: pchar := alloc(1024) as pchar; toArray(pipe, json); stringify(back, pipe); PrintLn(StrConcat(StrConcat(json, " -> "), StrConcat(pipe, StrConcat(" -> ", back)))); } fn main(): int64 { Zeig("[1,2,3]"c); Zeig("[\"a\",\"b\"]"c); Zeig("[1,[2,3]]"c); Zeig("[\"a|b\",\"c\"]"c); return 0; } [1,2,3] -> 1|2|3 -> ["1","2","3"] ["a","b"] -> "a"|"b" -> ["\"a\"","\"b\""] [1,[2,3]] -> 1|[2,3] -> ["1","[2,3]"] ["a|b","c"] -> "a|b"|"c" -> ["\"a","b\"","\"c\""] > **Der Durchlauf ist nicht verlustfrei** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1432|#1432]]). Vier Dinge gehen schief: > > - **Zahlen werden zu Zeichenketten**: ''%%[1,2,3]%%'' kommt als ''%%["1","2","3"]%%'' zurück, weil ''serializeArray'' jedes Element in Anführungszeichen setzt. > - **Zeichenketten werden doppelt maskiert**: die Anführungszeichen stehen im Pipe-Format noch drin. > - **Verschachtelung wird Text**: aus dem inneren ''%%[2,3]%%'' wird die Zeichenkette ''%%"[2,3]"%%''. > - **Ein ''|'' im Wert erzeugt zusätzliche Elemente** — aus zwei werden drei. > > Brauchbar ist das Format also für **flache Arrays gleichartiger Werte ohne Pipe-Zeichen**. Wer Zahlen zurückbekommen will, liest die Pipe-Form selbst mit ''StrSplit'' aus, statt über ''stringify'' zu gehen. Die Pipe-Form lässt sich mit ''StrSplit'' aus [[lyx_-_programmiersprache:units:string|std.string]] weiterverarbeiten — das ist der vorgesehene Weg, an die einzelnen Werte zu kommen. ---- ===== Einzelwerte ===== ''parseValue'' erkennt den Typ am ersten Zeichen und legt den Wert in ''dest'' ab: ^ Eingabe ^ Rückgabe ^ ''dest'' ^ | ''%%"Hallo"%%'' | ''JSON_STRING'' (3) | ''Hallo'' — Anführungszeichen entfernt, Maskierungen aufgelöst | | ''%%[1,2]%%'' | ''JSON_ARRAY'' (4) | Pipe-Form ''%%1|2%%'' | | ''%%{"a":1}%%'' | ''JSON_OBJECT'' (5) | **unverändert** — der Puffer wird nicht angefasst | | ''true'' / ''false'' | ''JSON_BOOL'' (1) | ''true'' / ''false'' | | ''null'' | ''JSON_NULL'' (0) | ''null'' | | ''42'', ''3.5'' | ''JSON_NUMBER'' (2) | die Ziffern | | alles andere | ''JSON_NUMBER'' (2) | die Eingabe unverändert | > **Zwei Fallen in dieser Tabelle** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1435|#1435]]): Bei einem **Objekt** bleibt der Zielpuffer unberührt — dort steht dann noch, was ein vorheriger Aufruf hinterlassen hat. Und **unbekannte Eingaben werden als Zahl gemeldet**: ''Muell'' liefert ''JSON_NUMBER'' und landet unverändert im Puffer. Vor der Auswertung also den Typ prüfen **und** bei ''JSON_OBJECT'' den Puffer selbst leeren. ---- ===== Fallstricke ===== * **Zielpuffer gehören dem Aufrufer** und werden nicht auf Größe geprüft. Für ''JSONEscape'' sind zwei Byte je Eingabezeichen plus zwei für die Anführungszeichen sicher. * **''JSONEscape'' setzt die Anführungszeichen selbst** — wer sie zusätzlich schreibt, bekommt doppelte. * **''isValidJSON'' ist keine Eingangsprüfung** für fremde Daten. * **Kein Objektzugriff, keine Pfadausdrücke, keine Zahlen-Umwandlung** — was aus ''parseValue'' kommt, ist Text; für ''int64'' anschließend ''StrToInt'' aus [[lyx_-_programmiersprache:units:string|std.string]]. * **Kein Whitespace-Handling** zwischen Elementen: ''%%[1, 2]%%'' ergibt im Pipe-Format ''%%1| 2%%'' — mit führendem Leerzeichen im zweiten Element. * **''serializeValue'' maskiert nur bei ''JSON_STRING''.** Für alle anderen Typen kopiert es die Eingabe wörtlich — auch dann, wenn dort etwas steht, das kein gültiges JSON ist. ---- **Weiterführend:** [[lyx_-_programmiersprache:guides:daten-serialisierung|Guide: Daten & Serialisierung]] · [[lyx_-_programmiersprache:units:string|std.string]] · [[lyx_-_programmiersprache:units:xml|std.xml]] · [[lyx_-_programmiersprache:units:net:http|std.net.http]] · [[lyx_-_programmiersprache:units|Standard Library]] **Quelle:** ''std/json.lyx'' (480 Zeilen) · **Autor:** Andreas Röne · **Copyright:** 2024–2025 Andreas Röne Letzte Aktualisierung: 2026-08-13 — Seite gegen ''std/json.lyx'' überarbeitet: zwei lauffähige Beispiele mit echter Ausgabe, Rückgabewert-Bedeutungen je Funktion, Verhaltenstabelle für ''parseValue'', Grenzen des Pipe-Formats, Fallstricke. Belegt und als Issue erfasst: Pipe-Durchlauf verliert Typen ([[https://github.com/SEOLizer/LyX-Compiler/issues/1432|#1432]]), ''JSONEscape'' maskiert keine Steuerzeichen ([[https://github.com/SEOLizer/LyX-Compiler/issues/1433|#1433]]), ''isValidJSON'' prüft ''%%[…]%%''/''%%{…}%%'' nur auf Klammerpaare ([[https://github.com/SEOLizer/LyX-Compiler/issues/1434|#1434]]), ''parseValue'' lässt ''dest'' bei Objekten unberührt ([[https://github.com/SEOLizer/LyX-Compiler/issues/1435|#1435]]). Geprüft mit ''lyxc 1.0.21A''.