0%

JSON und CSV · Übung

JSON lesen und validieren

Das Einlesen beantwortet eine Frage: „Ist dieser Text JSON, und welche Python-Werte beschreibt er?“ Die Validierung beantwortet eine andere: „Erfüllen diese Werte unser Katalogschema?“ Erfolgreiches Einlesen ist notwendig, aber nicht ausreichend.

Das ist gültiges JSON:

{"message": "hello"}

Es ist kein Katalogdokument. Es hat die falschen Felder und kein Artikel-Array.

load liest eine Datei; loads liest einen String

Das s bezeichnet die String-Varianten:

Try it

json.loads(text) liest einen String ein, der bereits im Speicher liegt. Sein Gegenstück json.dumps(value) gibt einen JSON-String zurück. Verwende für die Projektdateien das Paar mit Dateihandles:

with open(path, encoding="utf-8") as handle:
    document = json.load(handle)

load und dump arbeiten jeweils mit einem geöffneten Dateiobjekt. Die Namen sind ähnlich; die Eingabe sagt dir, welcher passt.

Container vor ihrem Inhalt validieren

Beginne oben und arbeite dich nach innen vor:

  1. Der genaue Typ des Dokuments ist dict, mit genau schema_version und items.

  2. Der genaue Typ der Version ist int, und ihr Wert ist 1.

  3. items hat genau den Typ list.

  4. Jeder Artikel hat genau den Typ dict und genau die fünf Artikelfelder.

  5. Jedes Feld hat seinen festgelegten Typ und seine Gültigkeitsregel.

Das Wort genau ist bei booleschen Werten wichtig. In Python ist bool eine Unterklasse von int. Das hat eine Folge, die viele erst glauben, wenn sie sie sehen:

Try it

Die erste Zeile erklärt, warum eine Schemaprüfung mit isinstance problemlos true akzeptiert, wo sie eine Zahl wollte. Die dritte Zeile soll den Punkt nur einprägsamer machen.

Das Schema erlaubt true weder als Menge noch als Schemaversion. Verwende deshalb für diese Felder type(value) is int.

Akzeptiere bei unit_price genau int oder float, aber nicht bool. Lehne negative Werte ab und weise nicht endliche Fließkommazahlen mithilfe von math.isfinite zurück.

Nicht standardkonforme Zahlen ausschließen

Pythons JSON-Parser akzeptiert standardmäßig aus Kompatibilitätsgründen NaN und Infinity, obwohl dies keine Zahlen des JSON-Standards sind. Übergib eine Funktion für parse_constant, die ValueError auslöst:

def reject_constant(value):
    raise ValueError(f"invalid JSON number: {value}")


json.load(handle, parse_constant=reject_constant)

Jetzt bist du dran

Implementiere validate_document(document) und read_catalog_json(path). Aktualisiere write_catalog_json so, dass es das Dokument vor dem Schreiben validiert. Damit sichert dasselbe Schema beide Richtungen ab.

Gib das validierte Dokument sowohl aus der Validierungs- als auch aus der Lesefunktion zurück. Lass json.JSONDecodeError und deine Meldungen zu ValueError sichtbar. Lektion 6 entscheidet, wo der benutzerseitige Befehl sie abfängt.

json.load war erfolgreich. Was ist damit belegt?

Warum verwendet das Schema für quantity die Prüfung type(value) is int?

Aufgabe

Implementiere validate_document(document) und read_catalog_json(path). Rufe dann die Validierung aus write_catalog_json auf, bevor es seine Ausgabedatei öffnet.

Lehne fehlerhaftes JSON, nicht standardkonformes NaN/Infinity, falsche oder zusätzliche Felder, falsche Container, leeren Text oder Text mit umgebendem Leerraum, negative oder nicht endliche Zahlen und boolesche Werte in Ganzzahl- oder Zahlenfeldern ab. Gib das gültige Dokument unverändert zurück.