← Python/Bestanden en I/O
Les 5 van 8
MODULE 11 · 05◷ 30 min

JSON-documenten bewaren en gericht fouten afhandelen

Na deze les kun je
  • Geneste JSON-gegevens lezen en schrijven
  • Ontbrekende bestanden onderscheiden van ongeldige inhoud

Een instellingenbestand heeft meer structuur dan een platte tabel: een naam, een aan-uitkeuze en een lijst favoriete plekken. Je wilt die waarden bewaren en later als dezelfde soorten waarden terugkrijgen. JSON is geschikt voor zulke kleine, geneste documenten.

Een formaat tussen tekst en waarden

JSON beschrijft onder andere objecten, arrays, strings, getallen, booleans en null. In Python lees je die meestal als dictionaries, lijsten, strings, getallen, True of False en None. De schrijfwijze in het bestand is dus niet hetzelfde als Python-broncode.

De module json heeft twee handige paren functies. load leest uit een geopende tekststream en dump schrijft naar een stream. De varianten loads en dumps werken met een string; de laatste letter verwijst naar die stringvorm.

Voor één keuze lees je eerst de dictionary en wijzig je alleen de bedoelde sleutel. Het volgende voorbeeld schrijft daarna het hele document terug en leest het opnieuw: de naam en minuten blijven behouden. Open dus nooit alvast met w voordat het oude document succesvol is gelezen.

Een document lezen en één keuze wijzigen
route.json
{"stop":"Café","open":true,"later":null,"minutes":[0,8]}
lees.py
import json

with open("route.json", encoding="utf-8") as stream:
    route = json.load(stream)
print(route["stop"])
print(route["open"], route["later"])
print(sum(route["minutes"]))

route["open"] = False
with open("route.json", "w", encoding="utf-8") as stream:
    json.dump(route, stream, ensure_ascii=False)
with open("route.json", encoding="utf-8") as stream:
    saved = json.load(stream)
print(saved["open"], saved["stop"], sum(saved["minutes"]))
Verwachte uitvoer
Café
True None
8
False Café 8

Let op true en null in het bestand tegenover True en None in de uitvoer. De parser doet die omzetting; je hoeft woorden niet zelf te vervangen. Een string met een nul blijft bovendien iets anders dan het getal nul of de boolean false.

Een heel document schrijven

Open een uitvoerbestand in modus w met UTF8 en geef de waarde aan json.dump. Met ensure_ascii=False blijft bijvoorbeeld café leesbaar als gewone tekst in het bestand. Standaard mag de encoder dezelfde letters ook als Unicode-escapes schrijven; beide vormen betekenen dezelfde waarde.

Schrijf één compleet document, geen losse documenten achter elkaar met a. Twee dictionaries direct achter elkaar vormen samen geen geldige JSON-waarde. Een inspringing met indent kan helpen bij het lezen, maar verandert de gegevens niet.

Bij vergelijken kijken we naar de ingelezen waarde. De volgorde van objectvelden en witruimte rondom komma's bepalen die betekenis niet. De volgorde van een lijst hoort juist wél bij de gegevens en moet behouden blijven.

Ontbreekt het bestand of is het kapot?

Ongeldig en ontbrekend zijn andere problemen
kapot.json
{"stop": }
fout.py
import json
from pathlib import Path

before = Path("kapot.json").read_bytes()
for name in ["kapot.json", "niet-aanwezig.json"]:
    try:
        with open(name, encoding="utf-8") as stream:
            settings = json.load(stream)
        settings["open"] = False
        with open(name, "w", encoding="utf-8") as stream:
            json.dump(settings, stream)
    except json.JSONDecodeError:
        print("bestand bestaat, inhoud klopt niet")
    except FileNotFoundError:
        print("bestand ontbreekt")
print(Path("kapot.json").read_bytes() == before)
print(Path("niet-aanwezig.json").exists())
Verwachte uitvoer
bestand bestaat, inhoud klopt niet
bestand ontbreekt
True
False

Een JSONDecodeError zegt dat de aanwezige tekst niet als geldig JSON-document gelezen kan worden. Een FileNotFoundError zegt dat het openen al misging doordat het bestand ontbreekt. Vang alleen die tweede fout wanneer je bij een eerste start een standaardwaarde wilt gebruiken.

Een bestaande lege dictionary of false is een geldige inhoud. Met result or fallback vervang je zulke geldige waarden onterecht. Geef de fallback uitsluitend terug in de afgesproken ontbrekend-bestandtak en laat andere fouten zichtbaar.

Veelgemaakte fout

Alles in één brede except Exception stoppen maakt een beschadigd instellingenbestand onzichtbaar. Je programma lijkt met standaardinstellingen te werken, terwijl gegevens verloren zijn geraakt. Een gerichte except houdt het onderscheid uit het voorbeeld intact.

Voor één instelling lees je eerst de bestaande dictionary, pas je één sleutel aan en schrijf je het document terug. Behoud alle andere velden en lees het oude document voordat je het voor schrijven opent. Deze simpele aanpak is nog geen bescherming tegen een schrijffout of twee gelijktijdige schrijvers; veilig vervangen komt later in deze module.

AAN DE SLAG 0/4
1

Een document lezen

Maak read_document(path): lees het UTF8 JSON-document als Python-waarde en laat JSONDecodeError door bij ongeldige inhoud. Verander het bestand niet.

2

Een document schrijven

Maak write_document(path, data): vervang het bestand door één geldig JSON-document met gewone Unicode-letters (ensure_ascii=False). Behoud typen en lijstvolgorde, verander data niet en geef None terug.

3

Alleen bij ontbreken terugvallen

Maak read_or_default(path, fallback): lees normaal het document, maar geef bij FileNotFoundError exact het meegegeven fallback-object terug. Laat JSONDecodeError en IsADirectoryError door; wijzig fallback niet.

4

Eén optie wijzigen

Maak set_option(path, key, value): lees de bestaande JSON-dictionary, zet die sleutel op value en schrijf het document terug. Behoud andere velden en geef None. Ongeldige oude inhoud blijft ongewijzigd met JSONDecodeError.

Onthouden

  • JSON-tekst en een Python-waarde zijn twee verschillende representaties.
  • Een bestand bevat één compleet document; load en dump werken met streams.
  • Een ontbrekend bestand verdient een andere reactie dan ongeldige inhoud.

Hierna zoeken en ordenen we bestanden in mappen.

json_files.py
UitvoerPython
›
›_

Hier komt je resultaat.

Voer je code uit en ontdek
wat je hebt gemaakt.

0/4 stappen
EVEN MEEKIJKEN

Bekijk hoe het kan.

Je mag de oplossing bekijken. Deze poging telt dan niet mee als afgeronde les. Met Reset kun je daarna opnieuw zelf oefenen.