JSON und CSV · Übung
Die Werttypen bewahren
read_csv_rows weiß jetzt, dass jede Zeile fünf Zellen hat. Es gibt "0", "7.25" und "false" aber weiterhin als Strings zurück. Diese Strings direkt als JSON zu schreiben würde gültiges JSON mit der falschen Bedeutung erzeugen.
Die Umwandlung gehört zur Schnittstelle
Das Ausgabeschema legt jeden Python-Typ fest, bevor die JSON-Serialisierung ihn sieht:
{
"sku": "GM-204",
"name": "Dice, Set of 6",
"quantity": 0,
"unit_price": 7.25,
"in_stock": False,
}
json bildet dieses Dictionary später auf natürliche Weise auf JSON ab. Entscheidend ist, zuerst dieses Dictionary inhaltlich richtig aufzubauen.
Entferne bei sku und name den umgebenden Leerraum und lehne ein leeres Ergebnis ab. Verlange für quantity einen Text aus Dezimalziffern, bevor du int aufrufst. So bleiben Werte wie "4.5" und "many" ausgeschlossen. Lehne anschließend ein negatives Ergebnis ab.
Für unit_price verarbeitet float gewöhnliche Dezimalschreibweisen, akzeptiert aber auch Sonderwerte wie nan und inf. Das sind keine endlichen Preise, und das Schema schließt sie aus. math.isfinite(price) schließt diese Lücke.
Ein boolescher Wert ist nicht einfach nicht leerer Text
Das ist falsch:
in_stock = bool(row["in_stock"])
Es lohnt sich, den Fehler zu beobachten, statt mir nur zu glauben:
Sowohl "true" als auch "false" werden zu True, weil bool bei einem String fragt: „Steht etwas darin?“ Beide enthalten vier oder fünf Zeichen. Nur der leere String ist False.
Vergleiche stattdessen mit den beiden erlaubten Schreibweisen:
if row["in_stock"] == "true":
in_stock = True
elif row["in_stock"] == "false":
in_stock = False
else:
raise ValueError("in_stock must be true or false")
Die strenge Kleinschreibungsregel ist beabsichtigt. Fünf Schreibweisen stillschweigend zu akzeptieren macht vorgelagerte Fehler schwerer erkennbar.
Jetzt bist du dran
Implementiere parse_row(row, row_number) und read_catalog_csv(path).
parse_row muss ein neues Dictionary mit genau den fünf Schemaschlüsseln und den festgelegten Typen zurückgeben. Es darf die unverarbeitete Zeile nicht verändern. Jeder Fehler zu einem Zeilenwert muss row_number enthalten.
read_catalog_csv soll read_csv_rows aufrufen, jede Zeile in ihrer Reihenfolge umwandeln und die Artikelliste zurückgeben. Übergib die bei eins beginnenden logischen CSV-Zeilennummern ab 2.
Erstelle eine vorübergehende Kopie von catalog.csv, füge ein viertes Produkt mit anderen Werten als die bisherigen drei hinzu und rufe read_catalog_csv mit dem Pfad dieser Kopie auf. Prüfe, ob die umgewandelte Ausgabe deiner neuen Zeile folgt. Behalte die ursprüngliche dreizeilige catalog.csv für die Einreichung unverändert bei. Ein Konverter, der nur die Zeilen verarbeitet, neben denen er geschrieben wurde, ist noch kein Konverter.
Warum ist bool(row["in_stock"]) der falsche Weg, "false" zu lesen?
Warum solltest du nach dem Aufruf von float noch math.isfinite(price) prüfen?
Aufgabe
Implementiere parse_row(row, row_number) und read_catalog_csv(path).
Gib exakte Artikel-Dictionarys zurück: sku und name als Strings, quantity als ganze Zahl, unit_price als endliche Fließkommazahl und in_stock als echten booleschen Wert. Lehne leeren Text, ungültige oder negative Zahlen, nicht endliche Preise und jeden booleschen Text außer exakt kleingeschriebenem true oder false ab.