JSON ו-Python מדברים באותן צורות
JSON הוא פורמט ברירת המחדל עבור APIs, קובצי הגדרות והעברת נתונים ברשת. למזלם של מתכנתי Python, אובייקט JSON ממופה ישירות ל-dict של Python, מערך JSON ל-list, ומחרוזת JSON ל-str. ההתאמה הקרובה הזו היא הסיבה שקריאה וכתיבה של JSON ב-Python הן פעולה של שתי שורות.
המודול json מהספרייה הסטנדרטית מטפל בשני הכיוונים.
פענוח מחרוזת JSON
json.loads(text), כלומר "load string", מקבלת מחרוזת בפורמט JSON ומחזירה את אובייקט ה-Python שהיא מייצגת:
התוצאה היא dict רגיל. ניגשים למפתחות שלו כמו בכל dict: אין עטיפת JSON מיוחדת ואין מתודת .parse() על התוצאה.
אם המחרוזת אינה JSON תקין, json.loads זורקת json.JSONDecodeError. הודעת השגיאה כוללת את השורה והעמודה של הבעיה, ובדרך כלל זה מספיק כדי לזהות פסיק חסר או מירכאה שלא עברה escape.
כתיבת JSON כמחרוזת
json.dumps(data), כלומר "dump string", עושה את ההפך: היא מקבלת אובייקט Python ומחזירה מחרוזת JSON:
True, False ו-None של Python מתורגמים אוטומטית ל-true, false ו-null של JSON. מספרים ומחרוזות עוברים כמו שהם. רשימות הופכות למערכים, ו-dicts הופכים לאובייקטים.
הדפסה מעוצבת
פלט ברירת המחדל של json.dumps דחוס: מתאים להעברה ברשת, קשה לקריאה לבני אדם. העבירו indent=2 כדי לקבל משהו קריא יותר:
עוד כמה אפשרויות של dumps שכדאי להכיר:
sort_keys=True: ממיינת את מפתחות האובייקט לפי סדר אלפביתי. שימושי לפלט דטרמיניסטי (קובצי הגדרות, fixtures לבדיקות, diffs).ensure_ascii=False: כותבת תווים שאינם ASCII (é, ü, 中) כמו שהם, במקום רצפי\u. בדרך כלל הבחירה הנכונה לקובצי UTF-8.separators=(",", ":"): הפלט הצפוף ביותר האפשרי. בשילוב עםensure_ascii=Falseמתקבל ה-JSON הקומפקטי ביותר ב-UTF-8 שאפשר להפיק.
קריאת JSON מקובץ
json.load(file), בלי s, קוראת ישירות מאובייקט קובץ. הדפוס הנפוץ משלב אותה עם with open:
import json
with open("config.json") as f:
config = json.load(f)
print(config["version"])
אין צורך לקרוא קודם את כל הקובץ למחרוזת: json.load עוברת על אובייקט הקובץ בעצמה.
כתיבת JSON לקובץ
json.dump(data, file) היא המקבילה לכתיבה לקובץ:
import json
data = {"created": "2026-01-01", "items": [1, 2, 3]}
with open("state.json", "w") as f:
json.dump(data, f, indent=2)
האפשרות indent עובדת גם כאן. פתחו את הקובץ שנוצר ותראו מסמך JSON מעוצב יפה.
קריאת תגובת JSON מ-API
רוב ספריות ה-HTTP מחזירות bytes או טקסט, ואתם קוראים ל-json.loads כדי להפוך אותם לנתונים שאפשר לעבוד איתם:
import json
import urllib.request
with urllib.request.urlopen("https://api.example.com/users/1") as response:
text = response.read().decode("utf-8")
user = json.loads(text)
print(user["name"])
הספרייה requests (שמוסברת בנפרד) חוסכת צעד: יש לתגובה שלה מתודת .json() שקוראת ל-json.loads בשבילכם.
מה JSON יכול ומה לא יכול לייצג
מערכת הטיפוסים של JSON מצומצמת יותר מזו של Python. המיפוי בשני הכיוונים:
| Python | JSON |
|---|---|
dict | אובייקט |
list, tuple | מערך |
str | מחרוזת |
int, float | מספר |
True | true |
False | false |
None | null |
tuples חוזרים מהמעבר הלוך ושוב כרשימות, והעובדה שהם היו tuple הולכת לאיבוד. sets, מחלקות מותאמות ואובייקטי datetime אינם ניתנים לסריאליזציה כברירת מחדל, ותקבלו TypeError: Object of type X is not JSON serializable.
טיפול ב-datetime ובאובייקטים מותאמים
שתי גישות נפוצות.
המירו קודם למבנה בטוח ל-JSON. בצעו את ההמרה בקוד שלכם, ואז בצעו סריאליזציה ל-dict רגיל:
העבירו פונקציית default=. json.dumps קוראת לה עבור כל ערך שהיא לא יודעת איך לבצע לו סריאליזציה:
כשקוראים את הנתונים בחזרה, צריך להמיר בעצמכם את מחרוזות ה-ISO ל-datetime: JSON לא זוכר מה הם היו במקור.
מעבר הלוך ושוב של dict דרך JSON
בדיקה מהירה ש-dicts שורדים את המעבר:
הערכים שווים, אבל אלה אובייקטים שונים. לרוב זה בדיוק מה שרוצים: json.dumps ואחריה json.loads הן דרך זולה ליצור עותק עמוק של כל מבנה שתואם ל-JSON.
דוגמה מציאותית
סקריפט קטן שטוען קובץ הגדרות JSON, מעדכן שדה ושומר אותו בחזרה:
import json
from pathlib import Path
config_path = Path("settings.json")
# טעינה (עם ברירת מחדל אם הקובץ לא קיים).
if config_path.exists():
config = json.loads(config_path.read_text())
else:
config = {"theme": "dark", "last_opened": None}
# עדכון.
config["last_opened"] = "2026-01-15"
# שמירה, בפורמט מעוצב.
config_path.write_text(json.dumps(config, indent=2, ensure_ascii=False))
זה מחזור שלם של "קריאה, שינוי, כתיבה" של JSON בתריסר שורות.
כמה הרגלים
- השתמשו ב-
with open(...)לקבצים, תמיד. JSON הוא בסך הכול טקסט, וכל הכללים של עבודה עם קבצים חלים עליו. - העדיפו
indent=2לקבצים שבני אדם קוראים: הגדרות, fixtures, נתונים מיוצאים. וותרו עליו בתעבורת רשת, שבה חשובה הקומפקטיות. - הגדירו
ensure_ascii=Falseלפלט UTF-8 כדי ששמות עם ניקוד, אותיות מוטעמות או תווים שאינם לטיניים יישארו קריאים. - אמתו עם
try/except json.JSONDecodeErrorכשאתם מפענחים נתונים שלא אתם יצרתם.
הצעד הבא
JSON מטפל בנתוני מפתח-ערך. הכלי הבא באותו פרק הוא CSV: התמיכה המובנית של Python בפורמט הטבלאי שמאחורי רוב קובצי הייצוא מגיליונות אלקטרוניים שתיתקלו בהם.
שאלות נפוצות
איך מפענחים JSON ב-Python?
השתמשו ב-json.loads(text) עבור מחרוזת JSON או ב-json.load(file) עבור אובייקט קובץ. שניהם מחזירים dict של Python (או list, תלוי ב-JSON). לדוגמה: data = json.loads('{"name": "Rosa"}'), ואז data['name'] הוא 'Rosa'.
איך ממירים dictionary של Python ל-JSON?
json.dumps(my_dict) מחזירה מחרוזת JSON. json.dump(my_dict, file) כותבת ישירות לאובייקט קובץ. העבירו indent=2 כדי לקבל פלט מעוצב: json.dumps(data, indent=2).
מה ההבדל בין json.loads ל-json.load?
loads (עם s) מקבלת מחרוזת. load (בלי s) מקבלת אובייקט קובץ. אותו דבר לגבי dumps מול dump. ה-s מסמנת string, וזו הדרך הקלה ביותר לזכור מה זה מה.
איך מטפלים בתאריכים ובאובייקטים מותאמים ב-JSON?
ל-JSON אין טיפוס תאריך, אז העבירו תאריכים כמחרוזות ISO-8601 ופענחו אותם בחזרה ידנית. עבור מחלקות מותאמות, אפשר להעביר ל-json.dumps פונקציית default= שמחזירה ייצוג בטוח ל-JSON, או להמיר בעצמכם ל-dict לפני הסריאליזציה.