کار با فایلهای JSON در پایتون؛ خواندن، نوشتن و تبدیل دادهها

🗓 آخرین بهروزرسانی:
JSON یکی از رایجترین قالبها برای ذخیره و تبادل داده میان برنامهها، وبسایتها و APIها است. پایتون با ماژول داخلی json میتواند دادههای JSON را به ساختارهایی مانند دیکشنری و لیست تبدیل کند، آنها را تغییر دهد و دوباره در فایل ذخیره کند.
این مطلب، درس دوازدهم از دوره رایگان پایتون سطح متوسط است. در درس قبلی با خواندن و نوشتن فایلهای متنی آشنا شدیم. اکنون همان مهارت را برای ذخیره دادههای ساختاریافته با فرمت JSON به کار میبریم.
JSON چیست؟
JSON مخفف JavaScript Object Notation است. این قالب داده بهصورت متن ساده ذخیره میشود، اما ساختاری منظم برای نمایش شیءها، آرایهها، رشتهها، عددها و مقادیر منطقی دارد.
نمونه یک سند JSON:
{
"name": "Mehran",
"city": "Istanbul",
"age": 30,
"active": true,
"skills": ["Python", "WordPress", "SEO"]
}JSON در موارد زیر بسیار استفاده میشود:
- ذخیره تنظیمات برنامه
- ارسال و دریافت اطلاعات از API
- ذخیره کاربران، محصولات، سفارشها و مخاطبان
- انتقال داده میان زبانهای برنامهنویسی مختلف
- ساخت فایل پشتیبان سبک و خوانا
تفاوت JSON با دیکشنری پایتون
ظاهر JSON و دیکشنری پایتون شبیه است، اما یکسان نیستند. JSON یک قالب متنی مستقل از زبان برنامهنویسی است؛ درحالیکه دیکشنری یک نوع داده داخل برنامه پایتون است.
| ویژگی | JSON | دیکشنری پایتون |
|---|---|---|
| ماهیت | متن ساختاریافته | شیء در حافظه |
| کلیدها | باید رشته باشند | میتوانند انواع تغییرناپذیر باشند |
| مقدار منطقی | true و false | True و False |
| مقدار تهی | null | None |
| رشته | با کوتیشن دوتایی | کوتیشن تکی یا دوتایی |
| کاربرد | ذخیره و تبادل داده | پردازش داده در برنامه |
واردکردن ماژول json
ماژول json بخشی از کتابخانه استاندارد پایتون است و نیازی به نصب جداگانه ندارد:
import jsonچهار تابع اصلی ماژول json
| تابع | ورودی | خروجی | کاربرد |
|---|---|---|---|
json.dumps() | شیء پایتون | رشته JSON | تبدیل داده پایتون به متن JSON |
json.loads() | رشته JSON | شیء پایتون | تبدیل متن JSON به داده پایتون |
json.dump() | شیء پایتون و فایل | نوشتن در فایل | ذخیره مستقیم داده در فایل JSON |
json.load() | فایل JSON | شیء پایتون | خواندن مستقیم داده از فایل |
حرف s در dumps و loads به مفهوم String اشاره دارد؛ این دو تابع با رشته کار میکنند. نسخههای بدون s با فایل کار میکنند.
تبدیل دیکشنری پایتون به رشته JSON با dumps
import json
user = {
"name": "Mehran",
"city": "Istanbul",
"active": True
}
json_text = json.dumps(user)
print(json_text)
print(type(json_text))خروجی یک رشته است:
{"name": "Mehran", "city": "Istanbul", "active": true}
<class 'str'>در تبدیل، مقدار True پایتون به true در JSON تبدیل شده است.
نمایش خواناتر JSON با indent
برای افزودن فاصله و تورفتگی از پارامتر indent استفاده میکنیم:
json_text = json.dumps(user, indent=2)
print(json_text)خروجی:
{
"name": "Mehran",
"city": "Istanbul",
"active": true
}مقدارهای رایج برای indent برابر 2 یا 4 هستند.
حفظ متن فارسی با ensure_ascii
بهصورت پیشفرض، کاراکترهای غیرلاتین ممکن است به کدهای Unicode تبدیل شوند:
data = {"message": "سلام پایتون"}
print(json.dumps(data))برای خوانا ماندن متن فارسی، ensure_ascii=False را تنظیم میکنیم:
json_text = json.dumps(
data,
ensure_ascii=False,
indent=2
)
print(json_text)مرتبکردن کلیدها با sort_keys
product = {
"price": 420000,
"name": "ماوس",
"active": True
}
json_text = json.dumps(
product,
ensure_ascii=False,
indent=2,
sort_keys=True
)
print(json_text)sort_keys=True کلیدها را براساس ترتیب الفبایی مرتب میکند. این گزینه برای مقایسه فایلها و خروجیهای ثابت مفید است.
تبدیل رشته JSON به داده پایتون با loads
import json
json_text = '''
{
"name": "Mehran",
"age": 30,
"active": true,
"skills": ["Python", "Git"]
}
'''
user = json.loads(json_text)
print(user)
print(type(user))
print(user["name"])
print(user["skills"][0])شیء JSON به دیکشنری پایتون تبدیل میشود و میتوان مانند هر دیکشنری دیگری با آن کار کرد.
نگاشت نوعهای داده JSON و پایتون
| JSON | پایتون پس از loads یا load |
|---|---|
| object | dict |
| array | list |
| string | str |
| number صحیح | int |
| number اعشاری | float |
| true | True |
| false | False |
| null | None |
ذخیره دیکشنری در فایل JSON با dump
import json
settings = {
"theme": "dark",
"language": "fa",
"notifications": True
}
with open("settings.json", "w", encoding="utf-8") as file:
json.dump(
settings,
file,
ensure_ascii=False,
indent=2
)تابع dump() خروجی را مستقیماً در فایل مینویسد. استفاده از encoding="utf-8" و ensure_ascii=False برای متن فارسی توصیه میشود.
خواندن فایل JSON با load
import json
with open("settings.json", "r", encoding="utf-8") as file:
settings = json.load(file)
print(settings)
print(settings["theme"])پس از خواندن فایل، خروجی معمولاً یک دیکشنری یا لیست پایتون است.
ذخیره لیستی از دیکشنریها
در بسیاری از پروژهها، فایل JSON شامل لیستی از رکوردها است:
products = [
{"id": 1, "name": "کتاب", "price": 180000},
{"id": 2, "name": "هدفون", "price": 950000},
{"id": 3, "name": "ماوس", "price": 420000}
]
with open("products.json", "w", encoding="utf-8") as file:
json.dump(
products,
file,
ensure_ascii=False,
indent=2
)پیمایش داده خواندهشده از JSON
with open("products.json", "r", encoding="utf-8") as file:
products = json.load(file)
for product in products:
print(product["name"], product["price"])افزودن رکورد جدید به فایل JSON
برخلاف فایل متنی، معمولاً نباید یک شیء JSON را مستقیماً با حالت a به انتهای فایل اضافه کنیم؛ زیرا نتیجه ممکن است دیگر یک سند JSON معتبر نباشد.
روش صحیح:
- فایل را بخوانید.
- داده را در حافظه تغییر دهید.
- کل ساختار را دوباره ذخیره کنید.
import json
with open("products.json", "r", encoding="utf-8") as file:
products = json.load(file)
new_product = {
"id": 4,
"name": "کیبورد",
"price": 680000
}
products.append(new_product)
with open("products.json", "w", encoding="utf-8") as file:
json.dump(
products,
file,
ensure_ascii=False,
indent=2
)بهروزرسانی یک رکورد در فایل JSON
with open("products.json", "r", encoding="utf-8") as file:
products = json.load(file)
for product in products:
if product["id"] == 2:
product["price"] = 890000
break
with open("products.json", "w", encoding="utf-8") as file:
json.dump(products, file, ensure_ascii=False, indent=2)حذف یک رکورد از JSON
with open("products.json", "r", encoding="utf-8") as file:
products = json.load(file)
products = [
product
for product in products
if product["id"] != 3
]
with open("products.json", "w", encoding="utf-8") as file:
json.dump(products, file, ensure_ascii=False, indent=2)بررسی وجود فایل پیش از خواندن
با pathlib میتوان وجود فایل را بررسی کرد:
from pathlib import Path
import json
file_path = Path("products.json")
if file_path.exists():
with file_path.open("r", encoding="utf-8") as file:
products = json.load(file)
else:
products = []مدیریت FileNotFoundError
import json
try:
with open("users.json", "r", encoding="utf-8") as file:
users = json.load(file)
except FileNotFoundError:
users = []
print("فایل وجود نداشت؛ لیست خالی ساخته شد.")مدیریت JSONDecodeError
اگر فایل خالی یا ساختار JSON نامعتبر باشد، خطای JSONDecodeError رخ میدهد:
import json
try:
with open("data.json", "r", encoding="utf-8") as file:
data = json.load(file)
except FileNotFoundError:
data = []
except json.JSONDecodeError as error:
data = []
print("ساختار JSON معتبر نیست:", error)تابع امن برای خواندن فایل JSON
import json
from pathlib import Path
def load_json(file_path, default=None):
path = Path(file_path)
if default is None:
default = []
try:
with path.open("r", encoding="utf-8") as file:
return json.load(file)
except FileNotFoundError:
return default
except json.JSONDecodeError:
return defaultاستفاده:
products = load_json("products.json", default=[])
settings = load_json("settings.json", default={})تابع استاندارد برای ذخیره JSON
import json
from pathlib import Path
def save_json(file_path, data):
path = Path(file_path)
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("w", encoding="utf-8") as file:
json.dump(
data,
file,
ensure_ascii=False,
indent=2
)ذخیره ایمن با فایل موقت
اگر برنامه هنگام نوشتن فایل متوقف شود، ممکن است فایل اصلی ناقص شود. برای دادههای مهم میتوان ابتدا در فایل موقت نوشت و سپس آن را جایگزین کرد:
import json
from pathlib import Path
def save_json_safely(file_path, data):
path = Path(file_path)
temp_path = path.with_suffix(path.suffix + ".tmp")
with temp_path.open("w", encoding="utf-8") as file:
json.dump(data, file, ensure_ascii=False, indent=2)
temp_path.replace(path)اعتبارسنجی ساختار داده پس از خواندن
معتبر بودن نحو JSON بهمعنای معتبر بودن داده برای برنامه نیست. پس از خواندن باید ساختار مورد انتظار بررسی شود:
def validate_product(product):
required_keys = {"id", "name", "price"}
if not isinstance(product, dict):
return False
if not required_keys.issubset(product):
return False
if not isinstance(product["id"], int):
return False
if not isinstance(product["name"], str):
return False
if not isinstance(product["price"], (int, float)):
return False
return Trueپاکسازی رکوردهای نامعتبر
valid_products = [
product
for product in products
if validate_product(product)
]کار با دادههای تودرتو
{
"user": {
"name": "Mehran",
"address": {
"country": "Türkiye",
"city": "Istanbul"
}
},
"permissions": ["read", "write"]
}دسترسی در پایتون:
city = data["user"]["address"]["city"]
first_permission = data["permissions"][0]دسترسی امن به کلیدهای تودرتو
city = (
data.get("user", {})
.get("address", {})
.get("city", "ثبت نشده")
)نوعهای غیرقابل تبدیل مستقیم به JSON
همه انواع پایتون مستقیماً قابل تبدیل نیستند. برای نمونه، set، شیءهای کلاس، datetime و Path بهصورت پیشفرض قابل سریالسازی نیستند:
import json
skills = {"Python", "Git", "SQL"}
json.dumps(skills)این کد خطای زیر را ایجاد میکند:
TypeError: Object of type set is not JSON serializableتبدیل Set پیش از ذخیره
skills = {"Python", "Git", "SQL"}
json_text = json.dumps(list(skills))تبدیل datetime به رشته
from datetime import datetime
import json
order = {
"id": 101,
"created_at": datetime.now().isoformat()
}
json_text = json.dumps(order, indent=2)استفاده از default برای نوعهای سفارشی
from datetime import datetime
from pathlib import Path
import json
def json_default(value):
if isinstance(value, datetime):
return value.isoformat()
if isinstance(value, Path):
return str(value)
if isinstance(value, set):
return list(value)
raise TypeError(f"نوع {type(value).__name__} قابل تبدیل نیست")
data = {
"created_at": datetime.now(),
"file": Path("report.txt"),
"tags": {"python", "json"}
}
json_text = json.dumps(
data,
default=json_default,
ensure_ascii=False,
indent=2
)ذخیره شیء یک کلاس در JSON
class Product:
def __init__(self, product_id, name, price):
self.product_id = product_id
self.name = name
self.price = price
def to_dict(self):
return {
"id": self.product_id,
"name": self.name,
"price": self.price
}
product = Product(1, "کتاب", 180000)
json_text = json.dumps(
product.to_dict(),
ensure_ascii=False,
indent=2
)تبدیل JSON به شیء سفارشی با object_hook
def create_product(data):
if {"id", "name", "price"}.issubset(data):
return Product(data["id"], data["name"], data["price"])
return data
json_text = '{"id": 1, "name": "Book", "price": 180000}'
product = json.loads(json_text, object_hook=create_product)برای پروژههای ساده معمولاً نگهداری داده بهشکل دیکشنری خواناتر است. تبدیل خودکار به شیء زمانی مفید است که ساختار برنامه دقیق و کنترلشده باشد.
کنترل جداکنندهها با separators
برای ساخت JSON فشردهتر میتوان فاصلههای غیرضروری را حذف کرد:
compact_json = json.dumps(
data,
ensure_ascii=False,
separators=(",", ":")
)نسخه فشرده برای انتقال شبکه مناسب است؛ نسخه دارای indent برای خواندن و نگهداری دستی بهتر است.
یکی از کاربردهای رایج JSON، ذخیرهی دادههایی است که از سایتها جمع میکنید؛ در آموزش استخراج داده از سایت با پایتون نتیجهی اسکرپینگ را دقیقاً با همین json.dump در فایل ذخیره میکنیم.
تفاوت JSON با CSV
| ویژگی | JSON | CSV |
|---|---|---|
| ساختار تودرتو | پشتیبانی میکند | محدود |
| نوع داده | رشته، عدد، بولی، null، آرایه و شیء | اغلب متن جدولی |
| خوانایی انسانی | خوب | بسیار خوب برای جدول |
| حجم | معمولاً بیشتر | معمولاً کمتر |
| کاربرد مناسب | تنظیمات، API و داده پیچیده | داده جدولی و اکسل |
اشتباه رایج: استفاده از کوتیشن تکی در JSON
JSON معتبر به کوتیشن دوتایی نیاز دارد:
{"name": "Mehran"}نسخه زیر JSON معتبر نیست:
{'name': 'Mehran'}این ساختار ممکن است نمایش یک دیکشنری پایتون باشد، اما برای json.loads() معتبر نیست.
اشتباه رایج: نوشتن True، False و None در JSON
در JSON باید از true، false و null استفاده شود:
{
"active": true,
"deleted": false,
"middle_name": null
}اشتباه رایج: ویرگول انتهایی
JSON استاندارد ویرگول پس از آخرین عضو را نمیپذیرد:
{
"name": "Mehran",
"city": "Istanbul"
}اشتباه رایج: افزودن مستقیم چند سند JSON به یک فایل
کد زیر فایل استاندارد JSON تولید نمیکند:
with open("data.json", "a", encoding="utf-8") as file:
json.dump({"id": 1}, file)
json.dump({"id": 2}, file)خروجی به شکل دو شیء چسبیده خواهد بود. برای چند رکورد، از یک لیست JSON استفاده کنید یا برای دادههای جریانمحور با آگاهی کامل از قالب JSON Lines استفاده کنید.
آشنایی کوتاه با JSON Lines
در قالب JSON Lines یا NDJSON، هر خط یک سند JSON مستقل است:
{"id": 1, "name": "Book"}
{"id": 2, "name": "Mouse"}
{"id": 3, "name": "Keyboard"}خواندن خطبهخط:
records = []
with open("records.jsonl", "r", encoding="utf-8") as file:
for line in file:
if line.strip():
records.append(json.loads(line))JSON Lines با یک فایل JSON معمولی تفاوت دارد و باید پسوند یا مستندات آن روشن باشد.
پروژه اول: مدیریت مخاطبان با فایل JSON
در این پروژه مخاطبان در یک فایل ذخیره میشوند و با اجرای دوباره برنامه باقی میمانند.
import json
from pathlib import Path
FILE_PATH = Path("contacts.json")
def load_contacts():
try:
with FILE_PATH.open("r", encoding="utf-8") as file:
data = json.load(file)
return data if isinstance(data, list) else []
except FileNotFoundError:
return []
except json.JSONDecodeError:
return []
def save_contacts(contacts):
with FILE_PATH.open("w", encoding="utf-8") as file:
json.dump(
contacts,
file,
ensure_ascii=False,
indent=2
)
def add_contact(contacts):
name = input("نام: ").strip()
phone = input("شماره تماس: ").strip()
if not name or not phone:
print("نام و شماره تماس الزامی هستند.")
return
contact = {
"id": max((item["id"] for item in contacts), default=0) + 1,
"name": name,
"phone": phone
}
contacts.append(contact)
save_contacts(contacts)
print("مخاطب ذخیره شد.")
def show_contacts(contacts):
if not contacts:
print("مخاطبی ثبت نشده است.")
return
for contact in contacts:
print(
f'{contact["id"]}. '
f'{contact["name"]} - '
f'{contact["phone"]}'
)
def search_contacts(contacts):
query = input("نام مورد جستوجو: ").strip().lower()
results = [
contact
for contact in contacts
if query in contact["name"].lower()
]
show_contacts(results)
def main():
contacts = load_contacts()
while True:
print("\n1. افزودن مخاطب")
print("2. نمایش مخاطبان")
print("3. جستوجو")
print("4. خروج")
choice = input("انتخاب: ").strip()
if choice == "1":
add_contact(contacts)
elif choice == "2":
show_contacts(contacts)
elif choice == "3":
search_contacts(contacts)
elif choice == "4":
break
else:
print("گزینه نامعتبر است.")
if __name__ == "__main__":
main()پروژه دوم: مدیریت تنظیمات برنامه
import json
from pathlib import Path
SETTINGS_FILE = Path("settings.json")
DEFAULT_SETTINGS = {
"theme": "light",
"language": "fa",
"notifications": True,
"font_size": 16
}
def load_settings():
try:
with SETTINGS_FILE.open("r", encoding="utf-8") as file:
saved = json.load(file)
except (FileNotFoundError, json.JSONDecodeError):
saved = {}
settings = DEFAULT_SETTINGS.copy()
if isinstance(saved, dict):
settings.update(saved)
return settings
def save_settings(settings):
with SETTINGS_FILE.open("w", encoding="utf-8") as file:
json.dump(
settings,
file,
ensure_ascii=False,
indent=2
)
settings = load_settings()
settings["theme"] = "dark"
save_settings(settings)
print(settings)اگر میخواهید داده JSON را از طریق وب در اختیار برنامههای دیگر بگذارید، در آموزش Flask یک API کوچک میسازیم که دیکشنریهای پایتون را مستقیم بهصورت JSON برمیگرداند.
برای دیدن کار با JSON در یک پروژه واقعی، برنامه هواشناسی با پایتون را ببینید؛ در آن پاسخ JSON یک API آبوهوا را میخوانیم و از دیکشنریهای تودرتوی آن دما و پیشبینی را استخراج میکنیم.
تمرینهای درس
- یک دیکشنری شامل نام، سن و مهارتها بسازید و با
dumps()به رشته JSON تبدیل کنید. - یک رشته JSON را با
loads()به دیکشنری تبدیل و مقدار یک کلید را چاپ کنید. - فهرستی از کتابها را در فایل
books.jsonذخیره کنید. - فایل کتابها را بخوانید و فقط کتابهای دارای قیمت کمتر از مقدار مشخص را نمایش دهید.
- یک کتاب جدید به لیست اضافه و فایل را دوباره ذخیره کنید.
- تابعی بنویسید که هنگام نبودن فایل، مقدار پیشفرض برگرداند.
- خطای
JSONDecodeErrorرا برای فایل خراب مدیریت کنید. - یک مقدار
setرا به شکل قابل ذخیره در JSON تبدیل کنید. - برنامهای بسازید که تنظیمات تم، زبان و اندازه فونت را در JSON نگه دارد.
- پروژه مخاطبان را با قابلیت حذف و ویرایش مخاطب تکمیل کنید.
📌 همچنین بخوانید: بازار کار پایتون در ایران، دستورهای break، continue و pass در پایتون و ساخت ربات تلگرام با پایتون
پرسشهای متداول
تفاوت json.load و json.loads چیست؟
json.load() داده را از فایل میخواند؛ json.loads() یک رشته JSON را تبدیل میکند.
تفاوت json.dump و json.dumps چیست؟
json.dump() داده را داخل فایل مینویسد؛ json.dumps() یک رشته JSON برمیگرداند.
چرا متن فارسی در JSON به شکل کد Unicode دیده میشود؟
پارامتر ensure_ascii=False را همراه با فایل دارای کدگذاری utf-8 استفاده کنید.
چرا نمیتوان Set را مستقیماً ذخیره کرد؟
JSON نوع داده Set ندارد. پیش از ذخیره آن را به لیست تبدیل کنید یا برای default تابع تبدیل بنویسید.
آیا میتوان با حالت a به فایل JSON داده افزود؟
برای یک سند JSON معمولی، افزودن مستقیم توصیه نمیشود. ابتدا فایل را بخوانید، داده را تغییر دهید و کل ساختار را دوباره بنویسید.
برای فایل خالی چه خطایی رخ میدهد؟
معمولاً json.JSONDecodeError رخ میدهد؛ زیرا متن خالی یک سند JSON معتبر نیست.
آیا JSON برای ذخیره رمز عبور مناسب است؟
JSON بهخودیخود امنیت یا رمزگذاری ندارد. اطلاعات حساس نباید بهصورت متن ساده در فایل JSON ذخیره شوند.
جمعبندی
در این درس یاد گرفتیم دادههای پایتون را با dumps() به رشته JSON تبدیل کنیم، رشتههای JSON را با loads() بخوانیم، داده را با dump() در فایل ذخیره کنیم و با load() دوباره به برنامه برگردانیم. همچنین متن فارسی، مدیریت خطا، اعتبارسنجی ساختار، ویرایش رکوردها، نوعهای غیرقابل سریالسازی و ذخیره امن فایل را بررسی کردیم.
در درس بعدی با ماژولها و پکیجها در پایتون آشنا میشویم و یاد میگیریم چگونه کدهای یک پروژه را در چند فایل سازماندهی و دوباره استفاده کنیم.
ادامه مسیر یادگیری پایتون
برای یادگیری منظم، آموزشهای مرتبط زیر را بهترتیب دنبال کنید:
برای ذخیره پیشرفت در همه دستگاهها و دریافت گواهی پایان مسیر، رایگان ثبتنام کن یا وارد شو.