[{"data":1,"prerenderedAt":840},["ShallowReactive",2],{"blog-\u002Fblog\u002Fpython-json-csv-pickle-explained":3},{"id":4,"title":5,"body":6,"description":826,"difficulty":827,"extension":828,"framework":829,"frameworkSlug":62,"meta":830,"navigation":84,"order":142,"path":831,"qaPath":832,"seo":833,"stem":834,"subtopic":835,"topic":836,"topicSlug":837,"updated":838,"__hash__":839},"blog\u002Fblog\u002Fpython-json-csv-pickle-explained.md","Python Serialization Explained — JSON, CSV, and pickle for Saving and Exchanging Data",{"type":7,"value":8,"toc":816},"minimark",[9,14,31,35,57,269,284,288,306,386,389,393,407,573,584,588,593,721,724,728,735,753,756,760,768,772,812],[10,11,13],"h2",{"id":12},"python-serialization-explained","Python serialization, explained",[15,16,17,18,22,23,26,27,30],"p",{},"Serialization turns in-memory objects into bytes you can store or send, and back again.\nPython's standard library covers the three formats you'll meet constantly: ",[19,20,21],"strong",{},"JSON"," for\ninterchange, ",[19,24,25],{},"CSV"," for tabular data, and ",[19,28,29],{},"pickle"," for arbitrary Python objects. Each has\na clear use case — and pickle has a sharp security warning.",[10,32,34],{"id":33},"json-for-interchange","JSON for interchange",[15,36,37,41,42,45,46,49,50,45,53,56],{},[38,39,40],"code",{},"json"," is the go-to for talking to web APIs and config files — it's language-agnostic and\nhuman-readable. The four functions split by string vs file: ",[38,43,44],{},"dumps","\u002F",[38,47,48],{},"loads"," work with\nstrings, ",[38,51,52],{},"dump",[38,54,55],{},"load"," with file objects.",[58,59,64],"pre",{"className":60,"code":61,"language":62,"meta":63,"style":63},"language-python shiki shiki-themes github-light github-dark","import json\n\ndata = {\"name\": \"Ada\", \"langs\": [\"python\", \"c\"], \"active\": True}\n\ntext = json.dumps(data, indent=2)        # object → JSON string\nback = json.loads(text)                  # JSON string → object\n\nwith open(\"config.json\", \"w\") as f:\n    json.dump(data, f, indent=2)         # write to file\nwith open(\"config.json\") as f:\n    cfg = json.load(f)                   # read from file\n","python","",[38,65,66,79,86,140,145,172,186,191,220,238,255],{"__ignoreMap":63},[67,68,71,75],"span",{"class":69,"line":70},"line",1,[67,72,74],{"class":73},"szBVR","import",[67,76,78],{"class":77},"sVt8B"," json\n",[67,80,82],{"class":69,"line":81},2,[67,83,85],{"emptyLinePlaceholder":84},true,"\n",[67,87,89,92,95,98,102,105,108,111,114,117,120,122,125,128,131,133,137],{"class":69,"line":88},3,[67,90,91],{"class":77},"data ",[67,93,94],{"class":73},"=",[67,96,97],{"class":77}," {",[67,99,101],{"class":100},"sZZnC","\"name\"",[67,103,104],{"class":77},": ",[67,106,107],{"class":100},"\"Ada\"",[67,109,110],{"class":77},", ",[67,112,113],{"class":100},"\"langs\"",[67,115,116],{"class":77},": [",[67,118,119],{"class":100},"\"python\"",[67,121,110],{"class":77},[67,123,124],{"class":100},"\"c\"",[67,126,127],{"class":77},"], ",[67,129,130],{"class":100},"\"active\"",[67,132,104],{"class":77},[67,134,136],{"class":135},"sj4cs","True",[67,138,139],{"class":77},"}\n",[67,141,143],{"class":69,"line":142},4,[67,144,85],{"emptyLinePlaceholder":84},[67,146,148,151,153,156,160,162,165,168],{"class":69,"line":147},5,[67,149,150],{"class":77},"text ",[67,152,94],{"class":73},[67,154,155],{"class":77}," json.dumps(data, ",[67,157,159],{"class":158},"s4XuR","indent",[67,161,94],{"class":73},[67,163,164],{"class":135},"2",[67,166,167],{"class":77},")        ",[67,169,171],{"class":170},"sJ8bj","# object → JSON string\n",[67,173,175,178,180,183],{"class":69,"line":174},6,[67,176,177],{"class":77},"back ",[67,179,94],{"class":73},[67,181,182],{"class":77}," json.loads(text)                  ",[67,184,185],{"class":170},"# JSON string → object\n",[67,187,189],{"class":69,"line":188},7,[67,190,85],{"emptyLinePlaceholder":84},[67,192,194,197,200,203,206,208,211,214,217],{"class":69,"line":193},8,[67,195,196],{"class":73},"with",[67,198,199],{"class":135}," open",[67,201,202],{"class":77},"(",[67,204,205],{"class":100},"\"config.json\"",[67,207,110],{"class":77},[67,209,210],{"class":100},"\"w\"",[67,212,213],{"class":77},") ",[67,215,216],{"class":73},"as",[67,218,219],{"class":77}," f:\n",[67,221,223,226,228,230,232,235],{"class":69,"line":222},9,[67,224,225],{"class":77},"    json.dump(data, f, ",[67,227,159],{"class":158},[67,229,94],{"class":73},[67,231,164],{"class":135},[67,233,234],{"class":77},")         ",[67,236,237],{"class":170},"# write to file\n",[67,239,241,243,245,247,249,251,253],{"class":69,"line":240},10,[67,242,196],{"class":73},[67,244,199],{"class":135},[67,246,202],{"class":77},[67,248,205],{"class":100},[67,250,213],{"class":77},[67,252,216],{"class":73},[67,254,219],{"class":77},[67,256,258,261,263,266],{"class":69,"line":257},11,[67,259,260],{"class":77},"    cfg ",[67,262,94],{"class":73},[67,264,265],{"class":77}," json.load(f)                   ",[67,267,268],{"class":170},"# read from file\n",[15,270,271,272,275,276,45,278,280,281,283],{},"The mnemonic: the ",[38,273,274],{},"s"," in ",[38,277,44],{},[38,279,48],{}," is for ",[19,282,274],{},"tring.",[10,285,287],{"id":286},"jsons-type-limitations","JSON's type limitations",[15,289,290,291,110,294,297,298,301,302,305],{},"JSON only knows objects, arrays, strings, numbers, booleans, and null. Python types like\n",[38,292,293],{},"datetime",[38,295,296],{},"set",", and ",[38,299,300],{},"Decimal"," don't map directly — you provide a ",[38,303,304],{},"default"," function to\nserialize them, and keys become strings.",[58,307,309],{"className":60,"code":308,"language":62,"meta":63,"style":63},"import json\nfrom datetime import datetime\n\njson.dumps({\"t\": datetime.now()}, default=str)   # serialize datetime as its string\n\n# dict int keys become strings on round-trip:\njson.loads(json.dumps({1: \"a\"}))                 # {'1': 'a'} — key is now str!\n",[38,310,311,317,330,334,358,362,367],{"__ignoreMap":63},[67,312,313,315],{"class":69,"line":70},[67,314,74],{"class":73},[67,316,78],{"class":77},[67,318,319,322,325,327],{"class":69,"line":81},[67,320,321],{"class":73},"from",[67,323,324],{"class":77}," datetime ",[67,326,74],{"class":73},[67,328,329],{"class":77}," datetime\n",[67,331,332],{"class":69,"line":88},[67,333,85],{"emptyLinePlaceholder":84},[67,335,336,339,342,345,347,349,352,355],{"class":69,"line":142},[67,337,338],{"class":77},"json.dumps({",[67,340,341],{"class":100},"\"t\"",[67,343,344],{"class":77},": datetime.now()}, ",[67,346,304],{"class":158},[67,348,94],{"class":73},[67,350,351],{"class":135},"str",[67,353,354],{"class":77},")   ",[67,356,357],{"class":170},"# serialize datetime as its string\n",[67,359,360],{"class":69,"line":147},[67,361,85],{"emptyLinePlaceholder":84},[67,363,364],{"class":69,"line":174},[67,365,366],{"class":170},"# dict int keys become strings on round-trip:\n",[67,368,369,372,375,377,380,383],{"class":69,"line":188},[67,370,371],{"class":77},"json.loads(json.dumps({",[67,373,374],{"class":135},"1",[67,376,104],{"class":77},[67,378,379],{"class":100},"\"a\"",[67,381,382],{"class":77},"}))                 ",[67,384,385],{"class":170},"# {'1': 'a'} — key is now str!\n",[15,387,388],{},"That key-stringification is a frequent surprise; design around it for JSON-bound data.",[10,390,392],{"id":391},"csv-for-tabular-data","CSV for tabular data",[15,394,395,396,399,400,45,403,406],{},"The ",[38,397,398],{},"csv"," module handles the quoting and escaping that make hand-rolled comma-splitting\nfragile. ",[38,401,402],{},"DictReader",[38,404,405],{},"DictWriter"," work with column names, which is usually clearest.",[58,408,410],{"className":60,"code":409,"language":62,"meta":63,"style":63},"import csv\n\nwith open(\"people.csv\", \"w\", newline=\"\") as f:\n    writer = csv.DictWriter(f, fieldnames=[\"name\", \"age\"])\n    writer.writeheader()\n    writer.writerow({\"name\": \"Ada\", \"age\": 36})\n\nwith open(\"people.csv\", newline=\"\") as f:\n    for row in csv.DictReader(f):\n        print(row[\"name\"], row[\"age\"])   # values are strings — convert as needed\n",[38,411,412,419,423,454,482,487,510,514,538,552],{"__ignoreMap":63},[67,413,414,416],{"class":69,"line":70},[67,415,74],{"class":73},[67,417,418],{"class":77}," csv\n",[67,420,421],{"class":69,"line":81},[67,422,85],{"emptyLinePlaceholder":84},[67,424,425,427,429,431,434,436,438,440,443,445,448,450,452],{"class":69,"line":88},[67,426,196],{"class":73},[67,428,199],{"class":135},[67,430,202],{"class":77},[67,432,433],{"class":100},"\"people.csv\"",[67,435,110],{"class":77},[67,437,210],{"class":100},[67,439,110],{"class":77},[67,441,442],{"class":158},"newline",[67,444,94],{"class":73},[67,446,447],{"class":100},"\"\"",[67,449,213],{"class":77},[67,451,216],{"class":73},[67,453,219],{"class":77},[67,455,456,459,461,464,467,469,472,474,476,479],{"class":69,"line":142},[67,457,458],{"class":77},"    writer ",[67,460,94],{"class":73},[67,462,463],{"class":77}," csv.DictWriter(f, ",[67,465,466],{"class":158},"fieldnames",[67,468,94],{"class":73},[67,470,471],{"class":77},"[",[67,473,101],{"class":100},[67,475,110],{"class":77},[67,477,478],{"class":100},"\"age\"",[67,480,481],{"class":77},"])\n",[67,483,484],{"class":69,"line":147},[67,485,486],{"class":77},"    writer.writeheader()\n",[67,488,489,492,494,496,498,500,502,504,507],{"class":69,"line":174},[67,490,491],{"class":77},"    writer.writerow({",[67,493,101],{"class":100},[67,495,104],{"class":77},[67,497,107],{"class":100},[67,499,110],{"class":77},[67,501,478],{"class":100},[67,503,104],{"class":77},[67,505,506],{"class":135},"36",[67,508,509],{"class":77},"})\n",[67,511,512],{"class":69,"line":188},[67,513,85],{"emptyLinePlaceholder":84},[67,515,516,518,520,522,524,526,528,530,532,534,536],{"class":69,"line":193},[67,517,196],{"class":73},[67,519,199],{"class":135},[67,521,202],{"class":77},[67,523,433],{"class":100},[67,525,110],{"class":77},[67,527,442],{"class":158},[67,529,94],{"class":73},[67,531,447],{"class":100},[67,533,213],{"class":77},[67,535,216],{"class":73},[67,537,219],{"class":77},[67,539,540,543,546,549],{"class":69,"line":222},[67,541,542],{"class":73},"    for",[67,544,545],{"class":77}," row ",[67,547,548],{"class":73},"in",[67,550,551],{"class":77}," csv.DictReader(f):\n",[67,553,554,557,560,562,565,567,570],{"class":69,"line":240},[67,555,556],{"class":135},"        print",[67,558,559],{"class":77},"(row[",[67,561,101],{"class":100},[67,563,564],{"class":77},"], row[",[67,566,478],{"class":100},[67,568,569],{"class":77},"])   ",[67,571,572],{"class":170},"# values are strings — convert as needed\n",[15,574,575,576,579,580,583],{},"Two gotchas: always open CSV files with ",[38,577,578],{},"newline=\"\""," (prevents blank rows on Windows), and\nremember every value reads back as a ",[19,581,582],{},"string",".",[10,585,587],{"id":586},"pickle-for-python-objects","pickle for Python objects",[15,589,590,592],{},[38,591,29],{}," serializes almost any Python object — nested structures, custom classes, even\nclosures-of-state — into bytes, preserving types exactly. It's Python-specific and binary.",[58,594,596],{"className":60,"code":595,"language":62,"meta":63,"style":63},"import pickle\n\nobj = {\"set\": {1, 2, 3}, \"tuple\": (1, 2)}        # types JSON can't represent\n\nblob = pickle.dumps(obj)                          # → bytes\nrestored = pickle.loads(blob)                     # exact types preserved\n\nwith open(\"state.pkl\", \"wb\") as f:                # note binary mode\n    pickle.dump(obj, f)\n",[38,597,598,605,609,656,660,673,686,690,716],{"__ignoreMap":63},[67,599,600,602],{"class":69,"line":70},[67,601,74],{"class":73},[67,603,604],{"class":77}," pickle\n",[67,606,607],{"class":69,"line":81},[67,608,85],{"emptyLinePlaceholder":84},[67,610,611,614,616,618,621,624,626,628,630,632,635,638,641,644,646,648,650,653],{"class":69,"line":88},[67,612,613],{"class":77},"obj ",[67,615,94],{"class":73},[67,617,97],{"class":77},[67,619,620],{"class":100},"\"set\"",[67,622,623],{"class":77},": {",[67,625,374],{"class":135},[67,627,110],{"class":77},[67,629,164],{"class":135},[67,631,110],{"class":77},[67,633,634],{"class":135},"3",[67,636,637],{"class":77},"}, ",[67,639,640],{"class":100},"\"tuple\"",[67,642,643],{"class":77},": (",[67,645,374],{"class":135},[67,647,110],{"class":77},[67,649,164],{"class":135},[67,651,652],{"class":77},")}        ",[67,654,655],{"class":170},"# types JSON can't represent\n",[67,657,658],{"class":69,"line":142},[67,659,85],{"emptyLinePlaceholder":84},[67,661,662,665,667,670],{"class":69,"line":147},[67,663,664],{"class":77},"blob ",[67,666,94],{"class":73},[67,668,669],{"class":77}," pickle.dumps(obj)                          ",[67,671,672],{"class":170},"# → bytes\n",[67,674,675,678,680,683],{"class":69,"line":174},[67,676,677],{"class":77},"restored ",[67,679,94],{"class":73},[67,681,682],{"class":77}," pickle.loads(blob)                     ",[67,684,685],{"class":170},"# exact types preserved\n",[67,687,688],{"class":69,"line":188},[67,689,85],{"emptyLinePlaceholder":84},[67,691,692,694,696,698,701,703,706,708,710,713],{"class":69,"line":193},[67,693,196],{"class":73},[67,695,199],{"class":135},[67,697,202],{"class":77},[67,699,700],{"class":100},"\"state.pkl\"",[67,702,110],{"class":77},[67,704,705],{"class":100},"\"wb\"",[67,707,213],{"class":77},[67,709,216],{"class":73},[67,711,712],{"class":77}," f:                ",[67,714,715],{"class":170},"# note binary mode\n",[67,717,718],{"class":69,"line":222},[67,719,720],{"class":77},"    pickle.dump(obj, f)\n",[15,722,723],{},"Unlike JSON, sets and tuples survive the round-trip intact.",[10,725,727],{"id":726},"never-unpickle-untrusted-data","Never unpickle untrusted data",[15,729,730,731,734],{},"This is the critical rule: ",[19,732,733],{},"unpickling executes arbitrary code",". A malicious pickle can run\nanything on your machine, so only load pickles you created or fully trust.",[58,736,738],{"className":60,"code":737,"language":62,"meta":63,"style":63},"# DANGER: a crafted pickle can run os.system(...) during loads()\npickle.loads(data_from_the_internet)    # ← never do this\n",[38,739,740,745],{"__ignoreMap":63},[67,741,742],{"class":69,"line":70},[67,743,744],{"class":170},"# DANGER: a crafted pickle can run os.system(...) during loads()\n",[67,746,747,750],{"class":69,"line":81},[67,748,749],{"class":77},"pickle.loads(data_from_the_internet)    ",[67,751,752],{"class":170},"# ← never do this\n",[15,754,755],{},"For data crossing a trust boundary or other languages, use JSON. Reserve pickle for caches\nand internal Python-to-Python transfer you control.",[10,757,759],{"id":758},"choosing-a-format","Choosing a format",[15,761,762,763,767],{},"JSON: interchange, APIs, config — portable and readable, limited types. CSV: tabular data for\nspreadsheets and data tools — strings only. pickle: full-fidelity Python objects, internal\nand trusted use only. When you need cross-language ",[764,765,766],"em",{},"and"," rich types, look at formats like\nMessagePack or Protocol Buffers, but the stdlib three cover most needs.",[10,769,771],{"id":770},"recap","Recap",[15,773,774,775,779,780,45,782,784,785,45,787,789,790,794,795,45,797,799,800,802,803,807,808,811],{},"Pick the format by purpose. ",[19,776,777],{},[38,778,40],{}," is for portable interchange (",[38,781,44],{},[38,783,48],{}," for\nstrings, ",[38,786,52],{},[38,788,55],{}," for files) but only handles basic types and stringifies dict keys.\n",[19,791,792],{},[38,793,398],{}," handles tabular data safely — use ",[38,796,402],{},[38,798,405],{},", open with\n",[38,801,578],{},", and convert the string values yourself. ",[19,804,805],{},[38,806,29],{}," serializes almost any\nPython object with exact types, but is binary, Python-only, and ",[19,809,810],{},"must never be used on\nuntrusted data"," because unpickling runs arbitrary code.",[813,814,815],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":63,"searchDepth":81,"depth":81,"links":817},[818,819,820,821,822,823,824,825],{"id":12,"depth":81,"text":13},{"id":33,"depth":81,"text":34},{"id":286,"depth":81,"text":287},{"id":391,"depth":81,"text":392},{"id":586,"depth":81,"text":587},{"id":726,"depth":81,"text":727},{"id":758,"depth":81,"text":759},{"id":770,"depth":81,"text":771},"How to serialize data in Python — the json module for interchange, the csv module for tabular data, pickle for Python objects, and the security and portability trade-offs between them.","medium","md","Python",{},"\u002Fblog\u002Fpython-json-csv-pickle-explained","\u002Fpython\u002Fstdlib\u002Fserialization",{"title":5,"description":826},"blog\u002Fpython-json-csv-pickle-explained","JSON, CSV & pickle","Standard Library Essentials","stdlib","2026-06-19","X1Aiq4b4iRTabBWWK2tk4g4UqRsEe5lL7UxVTLT_MKA",1782244093654]