آموزش پایتون, پایتون سطح متوسط

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

کار با فایل‌های 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 و falseTrue و False
مقدار تهیnullNone
رشتهبا کوتیشن دوتاییکوتیشن تکی یا دوتایی
کاربردذخیره و تبادل دادهپردازش داده در برنامه

واردکردن ماژول 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
objectdict
arraylist
stringstr
number صحیحint
number اعشاریfloat
trueTrue
falseFalse
nullNone

ذخیره دیکشنری در فایل 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 معتبر نباشد.

روش صحیح:

  1. فایل را بخوانید.
  2. داده را در حافظه تغییر دهید.
  3. کل ساختار را دوباره ذخیره کنید.
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

ویژگیJSONCSV
ساختار تو‌در‌توپشتیبانی می‌کندمحدود
نوع دادهرشته، عدد، بولی، 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 آب‌وهوا را می‌خوانیم و از دیکشنری‌های تودرتوی آن دما و پیش‌بینی را استخراج می‌کنیم.

تمرین‌های درس

  1. یک دیکشنری شامل نام، سن و مهارت‌ها بسازید و با dumps() به رشته JSON تبدیل کنید.
  2. یک رشته JSON را با loads() به دیکشنری تبدیل و مقدار یک کلید را چاپ کنید.
  3. فهرستی از کتاب‌ها را در فایل books.json ذخیره کنید.
  4. فایل کتاب‌ها را بخوانید و فقط کتاب‌های دارای قیمت کمتر از مقدار مشخص را نمایش دهید.
  5. یک کتاب جدید به لیست اضافه و فایل را دوباره ذخیره کنید.
  6. تابعی بنویسید که هنگام نبودن فایل، مقدار پیش‌فرض برگرداند.
  7. خطای JSONDecodeError را برای فایل خراب مدیریت کنید.
  8. یک مقدار set را به شکل قابل ذخیره در JSON تبدیل کنید.
  9. برنامه‌ای بسازید که تنظیمات تم، زبان و اندازه فونت را در JSON نگه دارد.
  10. پروژه مخاطبان را با قابلیت حذف و ویرایش مخاطب تکمیل کنید.

📌 همچنین بخوانید: بازار کار پایتون در ایران، دستورهای 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() دوباره به برنامه برگردانیم. همچنین متن فارسی، مدیریت خطا، اعتبارسنجی ساختار، ویرایش رکوردها، نوع‌های غیرقابل سریال‌سازی و ذخیره امن فایل را بررسی کردیم.

در درس بعدی با ماژول‌ها و پکیج‌ها در پایتون آشنا می‌شویم و یاد می‌گیریم چگونه کدهای یک پروژه را در چند فایل سازمان‌دهی و دوباره استفاده کنیم.

ادامه مسیر یادگیری پایتون

برای یادگیری منظم، آموزش‌های مرتبط زیر را به‌ترتیب دنبال کنید:

می‌خواهی پیشرفتت ذخیره شود؟با ورود یا ثبت‌نام رایگان، درس‌های تمام‌شده را علامت بزن و درصد پیشرفتت را در حساب کاربری ببین.ورود / ثبت‌نام برای ذخیره پیشرفت
درس بعدی · دوره پایتون متوسط (13 از 18)ماژول‌ها و پکیج‌ها در پایتون؛ آموزش import و ساخت Package ←
آموزش‌های تازه، کد آماده و رفع خطاهر هفته در کانال Dev Source؛ رایگان.
🧭 مسیر یادگیری پایتون از صفر
ماژول 7: مدیریت خطا و فایل‌ها — درس 33 از 44

برای ذخیره پیشرفت در همه دستگاه‌ها و دریافت گواهی پایان مسیر، رایگان ثبت‌نام کن یا وارد شو.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *