EXECUTED, WITH ASSERTIONS
This script was run during verification and its results asserted. It is the R script's cross-check: the same calculation, done a second way.
Straight from labs/course-6-r/python/08_file_io.py, unchanged.
"""Experiment 8 (Python equivalent) -- read/write CSV, JSON, XML.
R version: ../08_file_io.R (read.csv, jsonlite, XML, readxl)
Excel is omitted here: writing .xlsx needs a third-party library, and the point
of the experiment -- that each format round-trips -- is made by the other three.
"""
import csv, json, tempfile, pathlib
import xml.etree.ElementTree as ET
from _shared import STUDENTS
COLS = ["name", "section", "gender", "hours", "marks", "attendance"]
ROWS = [dict(zip(COLS, r)) for r in STUDENTS]
def csv_roundtrip(d):
p = d / "students.csv"
with open(p, "w", newline="") as fh:
w = csv.DictWriter(fh, fieldnames=COLS); w.writeheader(); w.writerows(ROWS)
with open(p, newline="") as fh:
back = list(csv.DictReader(fh))
# everything read from CSV is a string -- the same trap as in Course 3
return back, all(isinstance(v, str) for v in back[0].values())
def json_roundtrip(d):
p = d / "students.json"
p.write_text(json.dumps(ROWS, indent=2))
back = json.loads(p.read_text())
return back, isinstance(back[0]["marks"], int) # JSON preserves types
def xml_roundtrip(d):
p = d / "students.xml"
root = ET.Element("students")
for r in ROWS:
el = ET.SubElement(root, "student")
for k, v in r.items():
ET.SubElement(el, k).text = str(v)
ET.ElementTree(root).write(p, encoding="utf-8", xml_declaration=True)
back = [{c.tag: c.text for c in el} for el in ET.parse(p).getroot()]
return back, all(isinstance(v, str) for v in back[0].values())
if __name__ == "__main__":
# Step 1: Write and read back CSV, JSON and XML
with tempfile.TemporaryDirectory() as td:
d = pathlib.Path(td)
for name, fn in (("CSV", csv_roundtrip), ("JSON", json_roundtrip),
("XML", xml_roundtrip)):
back, typed = fn(d)
same = len(back) == len(ROWS) and back[0]["name"] == ROWS[0]["name"]
print(f"{name:<6} wrote {len(ROWS)} rows, read {len(back)} back "
f"-> {'round-trips ✓' if same else 'MISMATCH'}")
if name == "JSON":
print(f" numeric types preserved: {typed}")
else:
print(f" all values come back as strings: {typed}")
# Step 2: Compare the types that come back
print("\n KEY POINT: CSV and XML are untyped -- everything returns as text,")
print(" so numbers must be converted explicitly. JSON preserves numbers and")
print(" booleans. In R this is why read.csv() has colClasses= and why")
print(" jsonlite::fromJSON() gives you a usable data frame directly.")
The same thing in R: Reading and Writing CSV, Excel, JSON and XML in R.
The theory behind it is in this course’s units; the whole lab, with all 18 experiments, is on the lab page.