The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Encode a JSON stringtext = json.dumps(value)View examples
Decode a JSON stringvalue = json.loads(text)View examples
Use interoperable value typespayload = {'ok': True, 'items': [1, 2], 'note': None}View examples
Pretty-print deterministicallytext = json.dumps(value, indent=2, sort_keys=True, ensure_ascii=False)View examples
Emit compact JSONtext = json.dumps(value, separators=(',', ':'), ensure_ascii=False)View examples
Encode to a text streamjson.dump(value, stream)View examples
Decode from a streamvalue = json.load(stream)View examples
Encode a custom typetext = json.dumps(value, default=encode_custom)View examples
Transform decoded objectsvalue = json.loads(text, object_hook=decode_custom)View examples
Parse decimals exactlyvalue = json.loads(text, parse_float=Decimal)View examples
Reject non-finite outputtext = json.dumps(value, allow_nan=False)View examples
Inspect object key pairsvalue = json.loads(text, object_pairs_hook=reject_duplicates)View examples
Read CSV rowsrows = csv.reader(stream, delimiter=',')View examples
Write CSV rowswriter = csv.writer(stream, lineterminator='\n')View examples
Read named CSV fieldsrows = csv.DictReader(stream)View examples
Write named CSV fieldswriter = csv.DictWriter(stream, fieldnames=['name', 'note'])View examples

JSON and CSV are interchange formats, not snapshots of arbitrary Python objects. Define the wire shape, encoding, numeric policy, and CSV dialect at each boundary; validate untrusted input before trusting its contents; and make output deterministic when it will be tested, reviewed, cached, or signed. The standard library handles syntax, while your application remains responsible for schemas, size limits, and domain validation.

Step by step

Detailed examples

01

Translate deliberately between Python and JSON values

JSON objects, arrays, strings, numbers, booleans, and null map naturally to Python dictionaries, lists, strings, integers or floats, booleans, and None. Tuples encode as arrays and decode as lists, while JSON object names are strings, so a round trip can change tuple identity and non-string dictionary keys. JSON does not natively represent Decimal, datetime, sets, bytes, or arbitrary instances. Treat decoding as parsing rather than validation, and limit the byte size and nesting of untrusted documents before or around parsing.

Observe the JSON-compatible value model
import json

payload = {'active': True, 'tags': ('api', 'batch'), 'missing': None}
encoded = json.dumps(payload, sort_keys=True, separators=(',', ':'))
decoded = json.loads(encoded)

print(encoded)
print(type(decoded['tags']).__name__)
print(type(decoded['missing']).__name__)
Output
{"active":true,"missing":null,"tags":["api","batch"]}
list
NoneType
Back to quick reference ↑
02

Choose readable, compact, and Unicode output intentionally

indent makes nested data readable; separators controls optional whitespace; and sort_keys stabilizes object member order for tests and review. Since Python dictionaries preserve insertion order, default encoding also preserves their order, but that is not a canonicalization scheme for signatures. ensure_ascii=False keeps non-ASCII characters readable in the returned str; encode that text as UTF-8 at the byte boundary. These presentation options do not validate a schema or normalize numbers and Unicode for cryptographic canonicalization.

Render the same value for people and transport
import json

record = {'count': 2, 'city': 'São Paulo'}
pretty = json.dumps(record, indent=2, sort_keys=True, ensure_ascii=False)
compact = json.dumps(record, sort_keys=True, ensure_ascii=False, separators=(',', ':'))

print(pretty)
print(compact)
Output
{
  "city": "São Paulo",
  "count": 2
}
{"city":"São Paulo","count":2}
Back to quick reference ↑
03

Keep document framing separate from stream I/O

dump and load operate on file-like objects, whereas dumps and loads return or accept in-memory values. The encoder writes str, so a destination must accept text. JSON is not a framed protocol: repeated dump calls to one stream concatenate values into invalid single-document JSON. For multiple messages, use a specified envelope, a length-prefixed protocol, or a documented format such as newline-delimited JSON with one compact value per physical line.

Read, transform, and write one streamed document
import json
from io import StringIO

source = StringIO('{"items": [3, 1, 2]}')
document = json.load(source)
document['items'].sort()

destination = StringIO()
json.dump(document, destination, separators=(',', ':'))
print(destination.getvalue())
Output
{"items":[1,2,3]}
Back to quick reference ↑
04

Tag custom representations and reject unknown objects

The default callback handles values the built-in encoder cannot serialize. Return only JSON-compatible data and raise TypeError for unsupported values instead of silently converting everything to strings. object_hook receives every decoded object from the inside out; use an explicit, versioned tag and validate all required fields before reconstructing a domain value. Never treat a tag from untrusted input as permission to import a class or execute code.

Round-trip a tagged Decimal value
import json
from decimal import Decimal

def encode_custom(value):
    if isinstance(value, Decimal):
        return {'$type': 'decimal', 'value': str(value)}
    raise TypeError(f'unsupported type: {type(value).__name__}')

def decode_custom(value):
    if value.get('$type') == 'decimal' and set(value) == {'$type', 'value'}:
        return Decimal(value['value'])
    return value

encoded = json.dumps({'price': Decimal('79.90')}, default=encode_custom, sort_keys=True)
decoded = json.loads(encoded, object_hook=decode_custom)
print(encoded)
print(decoded['price'] + Decimal('0.10'))
Output
{"price": {"$type": "decimal", "value": "79.90"}}
80.00
Back to quick reference ↑
05

Make numeric and object-name policies explicit

parse_float can preserve decimal text as Decimal when binary floating-point rounding is inappropriate. Python accepts and emits NaN and infinities by default even though strict JSON does not; set allow_nan=False when encoding and use parse_constant to reject them while decoding. Duplicate object names keep only the last value by default. An object_pairs_hook can reject duplicates before information is discarded. None of these controls replaces application-level limits or schema validation.

Preserve decimals and reject ambiguous JSON
import json
from decimal import Decimal

def reject_duplicates(pairs):
    result = {}
    for key, value in pairs:
        if key in result:
            raise ValueError(f'duplicate key: {key}')
        result[key] = value
    return result

amounts = json.loads('{"subtotal": 0.1, "tax": 0.2}', parse_float=Decimal)
print(amounts['subtotal'] + amounts['tax'])

operations = (
    ('non-finite number', lambda: json.dumps(float('nan'), allow_nan=False)),
    ('duplicate key', lambda: json.loads('{"role": "user", "role": "admin"}', object_pairs_hook=reject_duplicates)),
)
for label, operation in operations:
    try:
        operation()
    except ValueError:
        print(f'rejected: {label}')
Output
0.3
rejected: non-finite number
rejected: duplicate key
Back to quick reference ↑
06

Treat a CSV dialect as part of the contract

CSV has families of dialects rather than one universal interpretation. Declare delimiters, quoting, escape behavior, and line endings when the producer and consumer contract requires them. Open real CSV files with newline='' so the csv module controls newline processing correctly. Readers normally return strings and do not validate columns or convert domain types. DictReader and DictWriter make column names explicit; configure extrasaction and validate missing, extra, oversized, or formula-like fields according to the downstream system.

Round-trip quoted dictionary records
import csv
from io import StringIO

rows = [
    {'name': 'Ada', 'note': 'uses, commas'},
    {'name': 'Lin', 'note': 'line one\nline two'},
]
buffer = StringIO(newline='')
writer = csv.DictWriter(buffer, fieldnames=['name', 'note'], lineterminator='\n')
writer.writeheader()
writer.writerows(rows)
print(repr(buffer.getvalue()))

buffer.seek(0)
loaded = list(csv.DictReader(buffer))
print(loaded)
Output
'name,note\nAda,"uses, commas"\nLin,"line one\nline two"\n'
[{'name': 'Ada', 'note': 'uses, commas'}, {'name': 'Lin', 'note': 'line one\nline two'}]
Back to quick reference ↑

Local code tester

Explore JSON and CSV interchange

Encode a deterministic JSON document and round-trip CSV records entirely in memory.

Runs in your browser
Output
Press Run to load Python locally.

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. Python Software Foundationjson — JSON encoder and decoderdocs.python.org
  2. Python Software Foundationcsv — CSV File Reading and Writingdocs.python.org
  3. Python Software Foundationio — Core tools for working with streamsdocs.python.org
  4. Python Software Foundationdecimal — Decimal fixed-point and floating-point arithmeticdocs.python.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback