Модуль 5 · Урок 18 із 58
JSON і CSV: schema перед business logic
Парсер підтверджує лише синтаксис формату. Успішний json.load або csv.DictReader ще не доводить required fields, types, allowed values, uniqueness чи business grain. Надійний pipeline розділяє decode, structural validation, normalization і domain rules.
Безпечна практика модуля 5
Пакет містить тільки synthetic JSON/CSV, explicit UTF-8, schema validation, deterministic output, safe logging і локальні tests. Жодних персональних чи production-даних, secrets, мережевих викликів або зовнішніх залежностей.
JSON: decode не дорівнює validation
import json
from pathlib import Path
with Path("records.json").open(encoding="utf-8") as handle:
payload = json.load(handle)
if not isinstance(payload, list):
raise ValueError("root must be a list")json.load перетворює document на Python values, але не знає ваш domain. Після root type перевірте shape кожного record, required/unknown fields, value types, allowed labels і duplicate IDs. Не десеріалізуйте недовірені дані через pickle: такий payload може виконувати code.
Serialization потребує stable policy
json.dumps(
report,
ensure_ascii=False,
indent=2,
sort_keys=True,
) + "\n"ensure_ascii=False зберігає український текст читабельним у UTF-8; sort_keys=True може стабілізувати object-key output, але не виправляє nondeterministic list order. JSON object keys є strings; custom types на кшталт Path або datetime потребують explicit conversion contract.
CSV — tabular contract із newline=””
import csv
with Path("records.csv").open(
encoding="utf-8",
newline="",
) as handle:
reader = csv.DictReader(handle)
rows = list(reader)Документація csv вимагає відкривати file з newline="", щоб module correctly handled newlines. DictReader повертає string values: число "75" не стає int автоматично. Conversion має повертати exact field/row error, а не загальний «bad file».
Header — частина schema
Перевірте exact required headers, optional set, unknown columns, blank names і duplicates. Duplicate CSV headers небезпечні: mapping representation може втратити одне value ще до business validation. Якщо порядок columns є public export contract, зафіксуйте fieldnames явно.
required = {"id", "route", "minutes"}
headers = reader.fieldnames or []
if len(headers) != len(set(headers)):
raise ValueError("duplicate headers")
if not required.issubset(headers):
raise ValueError("missing required headers")Одна normalized model для двох formats
JSON і CSV adapters мають віддати ту саму normalized record shape. Business function не повинна знати, звідки прийшли дані. Це дозволяє contract tests: однакові synthetic records у JSON і CSV дають однаковий summary; invalid fixtures — однакові reason codes.
Definition of Done
- JSON root і record shape перевіряються до business logic.
- CSV відкривається з UTF-8 і
newline=""; headers мають exact policy. - Type conversion називає row/field/value без leak sensitive payload.
- Duplicate IDs і duplicate headers не overwrite-яться тихо.
- Output bytes стабільні для однакового normalized input.
Методичні джерела
Урок, сценарії, пояснення й вправи створені SEOWORK. Посилання ведуть лише на офіційну документацію Python.
Закріпіть матеріал уроку
Три сценарні питання. Для зарахування уроку потрібно дати щонайменше дві правильні відповіді.