0%

JSON en CSV · oefening

JSON lezen en valideren

Parsen beantwoordt één vraag: “Is deze tekst JSON, en welke Python-waarden beschrijft die?” Valideren beantwoordt een andere: “Voldoen die waarden aan ons catalogusschema?” Succesvol parsen is noodzakelijk, maar niet voldoende.

Dit is geldige JSON:

{"message": "hello"}

Het is geen catalogusdocument. Het heeft de verkeerde velden en geen itemarray.

load leest een bestand; loads leest een string

De s benoemt de stringversies:

Try it

json.loads(text) verwerkt een string die al in het geheugen staat. De tegenhanger json.dumps(value) geeft een JSON-string terug. Gebruik voor de projectbestanden het paar dat met handles werkt:

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

load en dump werken elk met één geopend bestandsobject. De namen lijken op elkaar; de invoer vertelt welke erbij hoort.

Valideer containers voordat je erin kijkt

Begin bovenaan en werk naar binnen:

  1. Het exacte type van het document is dict, met precies schema_version en items.

  2. Het exacte type van de versie is int en de waarde is 1.

  3. items heeft exact het type list.

  4. Elk item heeft exact het type dict en precies de vijf itemvelden.

  5. Elk veld heeft zijn afgesproken type en geldigheidsregel.

Het woord exact doet ertoe bij booleans. In Python is bool een subklasse van int, met een gevolg dat mensen zelden geloven voordat ze het zien:

Try it

De eerste regel verklaart waarom een schemacontrole met isinstance zonder bezwaar true accepteert waar een getal werd verwacht. De derde regel staat er alleen om het punt te laten hangen.

Het schema staat true niet toe als hoeveelheid of schemaversie, dus gebruik voor die velden type(value) is int.

Accepteer voor unit_price exact int of float, maar geen bool. Wijs negatieve waarden af en wijs met math.isfinite niet-eindige floats af.

Houd niet-standaardgetallen buiten

De JSON-parser van Python accepteert standaard NaN en Infinity om compatibiliteitsredenen, hoewel het geen getallen uit de JSON-standaard zijn. Geef een functie voor parse_constant mee die ValueError opwerpt:

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


json.load(handle, parse_constant=reject_constant)

Nu jij

Implementeer validate_document(document) en read_catalog_json(path). Werk write_catalog_json bij zodat die het document valideert vóór het schrijven. Hetzelfde schema bewaakt zo beide richtingen.

Geef het gevalideerde document terug uit zowel het valideren als het lezen. Laat json.JSONDecodeError en je ValueError-meldingen zichtbaar blijven; les 6 bepaalt waar de opdracht voor de gebruiker ze opvangt.

json.load is geslaagd. Wat is daarmee aangetoond?

Waarom gebruikt het schema type(value) is int voor quantity?

Opdracht

Implementeer validate_document(document) en read_catalog_json(path), en roep de validatie vervolgens vanuit write_catalog_json aan vóór het openen van het uitvoerbestand.

Wijs onjuist gevormde JSON, niet-standaard NaN/Infinity, verkeerde of extra velden, verkeerde containers, lege tekst of tekst met witruimte eromheen, negatieve of niet-eindige getallen en booleans in integer- of getalvelden af. Geef het geldige document ongewijzigd terug.