mitmario.dev

JSON

Python Sandbox 4 Min Lesezeit 5 BeispieleLektion 5 von 8

Der Aufräumer für den Download-Ordner soll eine neue Endung kennen, und dafür muss das Programm geändert werden. Seine Einstellungen stehen nämlich im Code, also welcher Ordner und welche Endung wohin. Nach genau solchen Änderungen läuft dann plötzlich nichts mehr.

Einstellungen gehören in eine eigene Datei, die das Programm beim Start liest. Eine CSV passt dafür schlecht, weil Einstellungen keine Tabelle sind. Darin steckt ein Dictionary mit Endungen, vielleicht eine Liste, ein Ja oder Nein. Für so etwas gibt es JSON (JavaScript Object Notation). Wie der Name sagt, kommt das aus JavaScript. Heute speichern aber Programme in fast jeder Sprache ihre Einstellungen und Daten darin, und die meisten Dienste im Netz antworten in JSON.

JSON ist Text

Ein Dictionary als JSON
import json

einstellungen = {
    "ordner": "Downloads",
    "probelauf": True,
    "zuletzt": None,
    "endungen": {".jpg": "Bilder", ".pdf": "Dokumente"},
    "hinweis": "Läuft freitags",
}
text = json.dumps(einstellungen)
print(text)
print(type(text))

json.dumps (dump string) macht aus einem Dictionary einen Text in JSON, und der sieht fast genauso aus wie das Dictionary. Die Unterschiede sind klein, aber wichtig. True und False heißen true und false, None heißt null, und Text steht immer in doppelten Anführungszeichen, auch bei den Schlüsseln.

JSON kennt Text, Zahlen, true, false, null, Listen in eckigen Klammern und Objekte in geschweiften. Objekte heißen sie in JSON, und sie sehen aus wie Dictionaries. Mehr gibt es nicht.

Das ä in „Läuft“ steht als \u00e4 da, seine Nummer in Unicode. Das ist gültiges JSON, und beim Einlesen kommt wieder ein ä heraus. Lesen kann es so nur kein Mensch. Weiter unten steht, wie man das abstellt.

Einlesen

Einstellungen einlesen
import json

with open("einstellungen.json", encoding="utf-8") as datei:
    einstellungen = json.load(datei)

print(einstellungen)
print(einstellungen["probelauf"], type(einstellungen["probelauf"]))
print(einstellungen["endungen"][".pdf"])

json.load liest JSON aus einer geöffneten Datei und baut daraus Python. Aus dem Objekt wird ein Dictionary, aus true wird True, und das innere Objekt mit den Endungen ist ein Dictionary im Dictionary. Abgefragt wird es wie jedes andere, mit zwei Schlüsseln hintereinander.

Anders als bei einer CSV ist hier nicht alles Text. true kommt als True heraus und eine Zahl als Zahl. Rechnen geht also ohne Umwandlung.

Ändern und zurückschreiben

Ändern und zurückschreiben
import json

with open("einstellungen.json", encoding="utf-8") as datei:
    einstellungen = json.load(datei)

einstellungen["probelauf"] = False
einstellungen["endungen"][".zip"] = "Archive"
einstellungen["hinweis"] = "Läuft freitags"

with open("einstellungen.json", "w", encoding="utf-8") as datei:
    json.dump(einstellungen, datei, indent=2, ensure_ascii=False)

with open("einstellungen.json", encoding="utf-8") as datei:
    print(datei.read())

Bei Einstellungen sind es immer drei Schritte. Alles einlesen, im Dictionary ändern, alles zurück in dieselbe Datei. json.dump schreibt in eine geöffnete Datei, und mit zwei Angaben wird das Ergebnis lesbar.

indent=2 rückt jede Ebene um zwei Leerzeichen ein, statt alles in eine lange Zeile zu schreiben. ensure_ascii=False lässt Umlaute als Umlaute in der Datei, statt \u00e4 daraus zu machen. Danach ist die Datei etwas, das man mit jedem Editor öffnen und verstehen kann, und das will man bei Einstellungen ja auch.

Damit stehen allerdings wieder echte Umlaute in der Datei, und die Kodierung aus Lektion 11.2 ist wieder wichtig. Ohne encoding="utf-8" schriebe Python unter Windows das € in der alten Windows-Kodierung, und ein Programm, das UTF-8 erwartet, scheitert daran. Deshalb steht sie beim Lesen und beim Schreiben dabei.

Die Reihenfolge ist dabei nicht egal. Durch das "w" beim Öffnen wird die Datei sofort geleert. Wenn du sie zum Schreiben öffnest, bevor du sie gelesen hast, liest du danach eine leere Datei, und die Einstellungen sind weg. Also erst lesen, dann schreiben, jedes in seinem eigenen with.

Der neue Eintrag „hinweis“ steht übrigens am Ende. Ein Dictionary behält die Reihenfolge, in der seine Schlüssel hineinkamen, und json.dump schreibt sie genau so.

Was JSON nicht kennt

Was JSON nicht kennt
import json
from decimal import Decimal

summe = Decimal("1204.00")
print(json.dumps({"monat": "09.2026", "wohnen": str(summe)}))
print(json.dumps({"monat": "09.2026", "wohnen": summe}))

Die erste Zeile klappt. Die zweite endet mit TypeError: Object of type Decimal is not JSON serializable, und seit Python 3.14 steht in der Zeile darunter, wo es passiert ist, when serializing dict item 'wohnen', also beim Eintrag „wohnen“.

Ein Decimal aus Lektion 10.5 gehört nicht zu dem, was JSON kennt, und Python rät nicht, was daraus werden soll. Bei einem Datum ist es genauso.

Beide macht man vorher selbst zu Text. Ein Decimal mit str(), das genau die Ziffern schreibt, die darin stehen, und ein Datum mit strftime. Wird die Datei später wieder eingelesen, macht Decimal("1204.00") aus "1204.00" wieder einen genauen Betrag. Über eine Kommazahl ginge das nicht, die ist nicht genau.

Von Hand bearbeitet

Von Hand bearbeitet
import json

with open("einstellungen.json", encoding="utf-8") as datei:
    einstellungen = json.load(datei)

Eine Datei mit Einstellungen wird früher oder später von Hand geändert. Und dabei passiert der häufigste Fehler in JSON überhaupt, ein Komma hinter dem letzten Eintrag. In Python ist das erlaubt, in JSON nicht.

Das Programm endet mit json.decoder.JSONDecodeError: Illegal trailing comma before end of object: line 3 column 21 (char 47). Auf Deutsch heißt das, ein Komma zu viel vor dem Ende des Objekts, in Zeile 3, Spalte 21. Python 3.12 meldet an dieser Stelle noch „Expecting property name enclosed in double quotes“ und zeigt auf die Zeile danach. Das ist ungefähr so hilfreich, wie es klingt.

Was die Aufgabe will

Das Haushaltsbuch bekommt eine neue Fassung, und dafür braucht es drei Änderungen an seinen Einstellungen. Gebraucht wird ein kleines Programm, das sie einliest, ändert und wieder zurückschreibt, ohne etwas anderes darin anzufassen.

Zum Mitnehmen

Eine JSON-Datei liest sich fast wie Python-Code, nur stehen true, false und null dort klein, und Texte haben nur doppelte Anführungszeichen. Herein kommt sie mit json.load(datei), zurück geht sie mit json.dump(daten, datei, indent=2, ensure_ascii=False), eingerückt und mit echten Umlauten. Decimal oder date kennt JSON nicht, die werden vorher zu Text.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 5 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, dein Code läuft auf einem Server

    Öffnet sich mit dem Basis Konto.