Your next chapter / Python UTF-8 JSON files and read-only validation

Foundation · 50 MINUTES · VERSION 1

Python UTF-8 JSON files and read-only validation

Separate file access, decoding, JSON syntax and data shape, with bounded reads and honest CLI failures.

Reviewed 23 Oct 2026

Your setup may differ. Windows, Ubuntu, and optional tool bundles →

Log in or create an account to keep private notes, bookmarks, and learning progress. Reading is free without an account.

What you will learn

  • Read a bounded UTF-8 local file using pathlib and a context manager.
  • Distinguish decoding, JSON syntax and data-shape validation.
  • Report CLI failure honestly without writing files or returning partial totals.

Theory: decoded does not mean valid

A file path identifies a local resource; its existence does not prove readable or valid content. Opening in a with block closes the file when the block exits, including on errors. Bytes must be decoded before JSON text is parsed.

Valid JSON can still be a number, null or the wrong object shape. This contract requires an object with a sessions list; each row must be an object with a valid minutes integer. Extra valid fields are ignored. Nonstandard NaN/Infinity constants are rejected. UTF-8 with an optional BOM is accepted deliberately for editor compatibility.

The program reads at most65537 bytes and rejects a file larger than65536 bytes before decoding. This is a small local practice boundary, not a complete secure upload service or a general defense against every malicious input. No arbitrary file paths are exposed through this website.

Worked example: a read-only JSON tool

Required: Python3.13+. Keep py_study_functions.py from the preceding lesson in the same personal folder. Save these two new files there. The tool reads the path you explicitly choose and writes no data file. Use dummy data, not customer exports.

py_study_json.py

"""Read bounded dummy JSON locally. This program never writes a data file."""
import json
from pathlib import Path
import sys

from py_study_functions import read_minutes


def reject_constant(value: str) -> None:
    raise ValueError("Use standard JSON constants.")


def total_from_file(path: Path) -> int:
    with path.open("rb") as stream:
        raw = stream.read(65537)
    if len(raw) > 65536:
        raise ValueError("Use at most 65536 bytes of study data.")
    try:
        data = json.loads(raw.decode("utf-8-sig"), parse_constant=reject_constant)
    except RecursionError:
        raise ValueError("Document nesting is too deep.") from None
    if not isinstance(data, dict) or not isinstance(data.get("sessions"), list):
        raise ValueError("Provide an object containing a sessions list.")
    total = 0
    for row in data["sessions"]:
        if not isinstance(row, dict):
            raise ValueError("Every session must be an object.")
        total += read_minutes(row.get("minutes"))
    return total


def main(arguments: list[str]) -> int:
    if len(arguments) != 1:
        print("Usage: python py_study_json.py <dummy-json-file>", file=sys.stderr)
        return 2
    try:
        total = total_from_file(Path(arguments[0]))
    except (OSError, ValueError):
        print("Cannot read study data: use an accessible UTF-8 JSON file with valid sessions.", file=sys.stderr)
        return 1
    print(f"Total minutes: {total}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main(sys.argv[1:]))

py_study_rows.json

{
    "sessions": [
        {"minutes": 15},
        {"minutes": 30},
        {"minutes": 0}
    ]
}

Run and inspect success or failure

Windows PowerShell:

python ./py_study_json.py ./py_study_rows.json

Ubuntu Bash:

python3 ./py_study_json.py ./py_study_rows.json
Total minutes: 45

Run from the files’ directory with the interpreter on PATH. Quote a path containing spaces in either shell. Windows Python3.13 was executed; Ubuntu execution is unverified.

Exit0 means a complete validated total was printed. Exit1 means a read/validation failure and no success total. Exit2 means the argument count is wrong. The CLI prints a short error to stderr rather than dumping file contents or credentials. The reader itself raises useful exceptions so tests can distinguish invalid bytes, syntax and shape. A missing file is an I/O failure, not an empty successful report.

Guided lab

  1. Predict45 for the supplied file and0 for an empty sessions list.
  2. Try malformed JSON, non-UTF-8 bytes, a top-level list, missing sessions and non-object rows.
  3. Reject True, text, null and out-of-range minutes even when JSON decoding succeeds.
  4. Test a file above the byte limit, a file with a space in its name and a UTF-8 BOM. Confirm inputs are never rewritten.
  5. Run without arguments and with a missing path. Inspect exit code, stderr and absence of a false success message. Do not treat every failed read as zero minutes.

Independent exercise

Add a dry-run report showing valid session count and total without changing the file. Keep one explicit failure policy and the same integer rule. Do not silently skip invalid rows.

Test empty data, a later invalid row, repeated reads, unchanged file bytes and an inaccessible/missing path. Hint: separate file access, shape validation and calculation so each boundary can be tested. If you later add writes, design atomic replacement/backup and permissions separately; this read-only lab does not implement them.

Common mistakes and interview

Avoid assuming valid JSON has the right fields, accepting bool as minutes, reading unbounded exports, hiding missing files as empty success, logging raw documents, and changing source files during a validation-only operation.

Interview: “How would you validate an imported data file?” Outline byte/encoding limits, syntax, shape, field types, failure policy, no partial success and test fixtures. State the difference between local CLI access and a public upload endpoint.

English practice and glossary

Path: a resource location. Encoding: mapping bytes to text. JSON parsing: converting JSON text to values. Schema/shape: required structure. Context manager: deterministic resource cleanup. Exit code: process success/failure signal.

Write: “The file decoded, but validation failed because __. The tool returned __ and did not __.” Save dummy evidence in your journal.

Recap and next step

Validate each boundary and keep failure distinct from empty valid data. This local tool is read-only and bounded. Next automate its contract checks with unittest and disposable fixtures.

Reveal exercise hints and self-check answers

Decoded JSON is not automatically a sessions object. Missing files, invalid UTF-8, bad syntax and wrong shape fail. Valid empty sessions total0; a supplied file with15/30/0 totals45. The tool does not write data and returns no completed total after failure.

Exercise outline: return a structured count/total only after every row validates. Keep file bytes unchanged and assert that in tests. Do not add automatic writes or treat error as a valid zero.

Official references

Practise on your own computer. The exercise self-checks are not graded. This version also has a scored quiz for verified accounts. Signed-in learners can save reading, self-reported practice, private notes, and bookmarks.