0%

JSON and CSV · practice

Preserve the Value Types

read_csv_rows now knows that each row has five cells. It still returns "0", "7.25", and "false" as . Writing those strings straight to JSON would produce valid JSON with the wrong meaning.

Conversion is part of the boundary

The output schema decides each Python type before the JSON serializer sees it:

{
    "sku": "GM-204",
    "name": "Dice, Set of 6",
    "quantity": 0,
    "unit_price": 7.25,
    "in_stock": False,
}

json will later map that to JSON naturally. The important work is making this dictionary truthful first.

For sku and name, remove surrounding whitespace and reject an empty result. For quantity, require decimal digit text before calling int. This keeps values such as "4.5" and "many" out. Then reject a negative result.

For unit_price, float handles ordinary decimal spellings, but it also accepts special values such as nan and inf. Those are not finite prices and the schema excludes them. math.isfinite(price) closes that gap.

A boolean is not non-empty text

This is wrong:

in_stock = bool(row["in_stock"])

It is worth watching that fail rather than taking my word for it:

Try it

Both "true" and "false" are True, because bool on a string asks “is there anything in it?” and both have five or four characters in them. Only the empty string is False.

Compare against the two permitted spellings instead:

if row["in_stock"] == "true":
    in_stock = True
elif row["in_stock"] == "false":
    in_stock = False
else:
    raise ValueError("in_stock must be true or false")

The strict lowercase rule is deliberate. Quietly accepting five spellings makes upstream mistakes harder to notice.

Your turn

Implement parse_row(row, row_number) and read_catalog_csv(path).

parse_row must return a new dictionary with exactly the five schema keys and the declared . It must not modify the raw row. Include row_number in every row- error.

read_catalog_csv should call read_csv_rows, convert each row in order, and return the item . Pass one-based logical CSV row numbers starting at 2.

Make a temporary copy of catalog.csv, add a fourth product with values unlike the existing three, and call read_catalog_csv with that copy’s path. Confirm the converted output follows your new row. Keep the original three-row catalog.csv unchanged for submission. A converter that only handles the rows it was written beside is not yet a converter.

Why is bool(row["in_stock"]) the wrong way to read "false"?

Why check math.isfinite(price) after calling float?

Task

Implement parse_row(row, row_number) and read_catalog_csv(path).

Return exact item with sku and name , quantity an integer, unit_price a finite float, and in_stock a real . Reject empty text, invalid or negative numbers, non-finite prices, and boolean text other than exact lowercase true or false.