Kiste
EN

Standard-Bibliothek — `json`

Verfügbar — implementiert in Phase B.6.6. Vollständige Spec in docs/json.md.

JSON ist das Lingua-Franca für Daten zwischen Programmen, APIs und Konfig-Dateien. Kistes json-Modul kann beide Richtungen: Text → Wert (parse) und Wert → Text (encode).

22.1 Aktivieren

nutze json

nimm daten = json.geparst("[1, 2, 3]")
sag daten           // [1, 2, 3]
sag typ(daten)      // liste

nimm text = json.erzeugt(daten)
sag text            // "[1,2,3]"

22.2 Number-Heuristik (das Spannende!)

JSON hat eine Zahl-Form. Kiste hat drei (ganz, dezimal, komma). Die Heuristik schaut auf die JSON-Eingabe:

JSON-Eingabe Kiste-Typ Begründung
42 (Integer-Form) ganz exakt, beliebig groß
42.5 (mit Punkt) dezimal „Ich habe 0.1 getippt → ich will exakte Dezimalrechnung"
1e6 oder 42.0e0 (mit e) komma „Wissenschaftliche Notation → ich rechne approximativ"
sag typ(json.geparst("42"))       // ganz
sag typ(json.geparst("42.5"))     // dezimal
sag typ(json.geparst("1e6"))      // komma

Warum diese Heuristik? Weil sie der Anwender-Intuition folgt. Wer 0.1 schreibt, will exakte Dezimalrechnung — dezimal ist die ehrliche Wahl. Wer 1e6 schreibt, hat sich bewusst für wissenschaftliche Notation entschieden — komma ist hier richtig.

22.3 Parse: zwei Varianten

// Variante 1: wirft bei Fehler
nimm daten = json.geparst("[1, 2, 3]")            // OK
nimm bumm  = json.geparst("kaputt")               // Fehler

// Variante 2: gibt nichts bei Fehler
nimm vielleicht = json.versucht_geparst("kaputt") // nichts
nimm gut        = json.versucht_geparst("[1,2]")  // [1, 2]

Wann welche? versucht_geparst ist für erwartbare Fehler — User-Eingabe, externe Daten, optionaler Lookup. geparst ist für Daten, die valid sein müssen — z.B. eine Konfig-Datei, die du selbst geschrieben hast: wenn die kaputt ist, soll das Programm laut scheitern, nicht still weitermachen.

22.4 Encode: kompakt oder schön

nimm person = {"name": "Sascha", "tags": ["admin", "geek"]}

sag json.erzeugt(person)
// {"name":"Sascha","tags":["admin","geek"]}

sag json.erzeugt_schön(person)
// {
//   "name": "Sascha",
//   "tags": [
//     "admin",
//     "geek"
//   ]
// }

// Custom-Einrückung
sag json.erzeugt_schön(person, "\t")    // mit Tab
sag json.erzeugt_schön(person, "    ")  // 4 Spaces

Seit 0.9.24 schreibt json.erzeugt selbst gebaute Karten und Listen auch im gebauten Programm (kiste build) korrekt — vorher gelang das dort nur mit Werten aus json.geparst.

22.4b Eine Liste von Karten aufbauen

Sammlungen gleichartiger Datensätze — die Bilder eines Sprite-Sheets, die Zeilen einer Tabelle, die Treffer einer Suche — sind in JSON ein Array von Objekten. In Kiste ist das eine Liste von Karten. Wichtig: Wer die Liste füllen will, startet sie leer. Eine Liste, die schon Karten enthält, lässt sich nachträglich nicht mehr erweitern.

nutze json
nutze liste

nimm rahmen = []                                  // leer starten!
für f = 0 bis 2 {
    liste.hänge_an(rahmen, {"x": f * 32, "dauer": 100})
}

nimm blatt = {"frames": rahmen, "meta": {"breite": 96, "schleife": wahr}}
sag json.erzeugt_schön(blatt, "  ")

Eine Liste von Karten darf auch direkt hingeschrieben werden — dann steht sie aber fest:

nimm punkte = [{"x": 0, "y": 0}, {"x": 5, "y": 3}]
sag json.erzeugt(punkte)      // [{"x":0,"y":0},{"x":5,"y":3}]

Und wieder heraus geht es mit zwei Zugriffen: erst die Karte aus der Liste, dann der Schlüssel aus der Karte.

nimm punkte = [{"x": 0, "y": 0}, {"x": 5, "y": 3}]

sag punkte[0]["x"]                       // 0
wiederhole p in punkte {
    sag "Punkt bei {p["x"]}, {p["y"]}"
}
0
Punkt bei 0, 0
Punkt bei 5, 3

Gut zu wissen — eine Karte in einer Liste wird nicht kopiert. Die Liste zeigt auf dieselbe Karte. Änderst du die Karte später, siehst du die Änderung auch über die Liste:

nimm m = {"titel": "Sonne"}
nimm werke = [m]
m["titel"] = "Mond"
sag werke[0]["titel"]                    // Mond — es ist dieselbe Karte
Mond

Für Listen in Listen gilt dasselbe. Zahlen, Texte und Wahrheitswerte verhalten sich anders: die werden kopiert.

22.5 Mapping-Tabelle (Kiste ↔ JSON)

Kiste JSON
nichts null
wahr / falsch true / false
ganz Integer-Form (42)
dezimal Dezimal-Form (42.5)
komma Float / e-Notation
text JSON-String (escaped)
liste JSON-Array
karte JSON-Object
Geld Fehler — Konvertiere vorher zu Karte oder text
Funktionen, Klassen, Instanzen Fehler — nicht serialisierbar
NaN / Inf Fehler — JSON kennt das nicht

22.6 Geld zu JSON — wie geht's?

Geld ist nicht JSON-native. Wer Geld serialisieren will, macht das explizit:

nutze geld
nutze json

nimm preis = geld.neu(19.95, "CHF")

// Variante 1: als Karte mit Betrag und Währung
nimm als_karte = {
    "betrag": geld.betrag(preis),
    "währung": geld.währung(preis),
}
sag json.erzeugt(als_karte)
// {"betrag":19.95,"währung":"CHF"}

// Variante 2: als Text
sag json.erzeugt(als_text(preis))
// "19.95 CHF"

Beide sind valid — die Wahl hängt davon ab, ob du beim Wieder-Lesen den Geld-Typ rekonstruieren willst (Karten-Form ist parser-freundlicher).

22.7 Roundtrip

nimm orig = {"name": "Sascha", "alter": 38}
nimm zurück = json.geparst(json.erzeugt(orig))
sag zurück["name"]   // "Sascha"
sag zurück["alter"]  // 38

Caveat zu Karten-Reihenfolge: JSON-Objects sind per RFC „unordered". Beim Encode behält Kiste die Insertion-Order der karte. Beim Parse geht die Original-Reihenfolge verloren — Schlüssel kommen alphabetisch sortiert raus. Wer Reihenfolge braucht, baut sie aus dem Roundtrip wieder auf (z.B. via liste von [schlüssel, wert]-Paaren).

22.8 Datei + JSON kombinieren

nutze json
nutze datei

nimm konfig = {"theme": "dunkel", "schriftgröße": 14}
datei.schreibe("config.json", json.erzeugt_schön(konfig))

// Später wieder einlesen
nimm gelesen = json.geparst(datei.inhalt("config.json"))
sag "Theme: {gelesen[\"theme\"]}"

22.9 Achtung: { und } in String-Literalen

In Kiste-Strings triggert {...} die String-Interpolation (E-018). Wenn du JSON direkt im Quelltext schreibst, musst du { und } mit Backslash escapen:

// Falsch: wird als Interpolation interpretiert
nimm s = "{\"a\":1}"

// Richtig: { und } escapen
nimm s = "\{\"a\":1\}"

In den meisten realen Fällen kommt JSON aus einer Datei oder einem Netzwerk-Aufruf — da gibt es das Problem nicht.

22.10 Was nicht in json ist

  • Streaming für sehr große Dateien — kommt mit bytes/Streaming-Phase.
  • JSON-Schema-Validierung — eigenes Modul.
  • JSON5 / JSONC / NDJSON — andere Formate.
  • Benutzer-definierte Type-Encoder (z.B. „Encode jedes Geld automatisch als Karte") — kann später als Optionen-Karte hinzukommen, falls Bedarf.