JSON and CSV
Turn external representations into validated Python values and export data without breaking its structure.
By the end, you’ll be able to…
- Choose JSON or CSV for a data exchange.
- Read and write structured data with standard library tools.
- Validate shape and convert values at an import boundary.
01Choose a representation for the shape
JSON represents nested objects, arrays, strings, numbers, booleans, and null. It suits a configuration containing settings and a list of tasks. CSV represents rows and columns and is convenient for spreadsheets or tabular exports. Neither format defines your application rules: valid JSON can still contain a negative quantity, and a valid CSV can omit a required column.
Keep serialization separate from business calculations. Decode the representation, validate its structure, convert values, and then pass clean data into the rest of your program. This boundary makes error messages precise and prevents every calculation from repeating defensive parsing.
02Serialization changes some Python types
JSON object keys are strings. Tuples become arrays and return as lists after decoding. Datetimes and custom class instances need an explicit representation, such as an ISO timestamp or a dictionary of fields. A round trip preserves the agreed data model, not every Python object identity or type.
Use dumps and loads for strings, and dump and load with file handles. Indentation helps humans review a saved file; it is not validation. Catch JSONDecodeError when reporting malformed input, then separately check expected keys and types. Avoid pickle for untrusted interchange because loading pickle can execute code.
03Let the CSV module handle delimiters
A field can contain a comma, a quote, or even a newline. Splitting a line on commas cannot distinguish a separator from a comma inside a quoted field. csv.DictReader maps header names to values, while DictWriter writes columns in an explicit order and applies the quoting rules.
Open CSV files with newline="" so the csv module controls newline handling. CSV readers ordinarily return strings, even when a cell looks numeric. Convert the fields you need, report the row number on failure, and decide how blank values are represented. A missing numeric value should not quietly become zero unless that is the documented rule.
04Design an import contract
For an import, document required headers, accepted value ranges, treatment of unknown columns, and whether one bad row rejects the whole file. A small internal tool may stop at the first error. A batch import may collect row-specific errors and reject all writes until validation succeeds. Both are defensible if the behavior is explicit.
Preserve identifiers as strings when leading zeros matter. A postal code or product code is not a quantity just because it contains digits. Also remember that CSV quoting does not make spreadsheet formulas harmless when a human opens an export; apply a separate spreadsheet-safe policy when exporting untrusted cell content for spreadsheet use.
The ideas worth keeping.
- Decode first, then validate the application contract.
- Use csv tools for quoting and newline handling.
- Convert quantities explicitly while preserving textual identifiers.
Make it yours.
Type and test your code right here in the interactive workspace. Run your solution, check the output, and compare with the worked guide when ready.
Validate a task payload
Decode JSON containing a title. Require a nonempty string after trimming and return the trimmed title. Reject missing titles and non-object roots.
Explore a worked solution
import jsondef task_title(text): value = json.loads(text) if not isinstance(value, dict): raise ValueError("expected an object") title = value.get("title") if not isinstance(title, str) or not title.strip(): raise ValueError("title must be nonempty text") return title.strip()assert task_title('{"title": " Learn CSV "}') == "Learn CSV"for text in ('[]', '{}', '{"title": " "}'): try: task_title(text) except ValueError: continue raise AssertionError("invalid input was accepted")JSON parsing establishes syntax only. The second phase enforces the application shape and normalizes the accepted title once.
Total an in-memory CSV
Use DictReader on CSV text to total quantity. Include an item containing a comma and keep its code 001 as text.
Explore a worked solution
import csvimport iotext = 'code,item,quantity\n001,"Tea, green",3\n002,Coffee,2\n'rows = list(csv.DictReader(io.StringIO(text, newline="")))assert rows[0]["code"] == "001"assert rows[0]["item"] == "Tea, green"total = sum(int(row["quantity"]) for row in rows)assert total == 5print(total)The reader understands quoted separators. Conversion is restricted to quantity, preserving identifier formatting.
What stayed with you?
Check the ideas while they’re fresh. Your answers stay in this page and feedback appears as you go.
Validate a task payload
Decode JSON containing a title. Require a nonempty string after trimming and return the trimmed title. Reject missing titles and non-object roots.
[Process exited with code 0 - No output]
Keep exploring.
The official references behind this chapter, when you want a second angle or the full details.
One chapter closer.
Reading is a start. Trying it yourself makes the connection.