Skip to content
العودة إلى المدوّنة
دروس تعليمية

تحويل JSON إلى Python: dataclass وPydantic وTypedDict (دليل 2026)

حوّل JSON إلى فئات Python بشكل صحيح: أنماط dataclass وPydantic v2 وTypedDict، مع تحديد Optional الدقيق والمزالق الشائعة. مجاني في المتصفح.

12 دقائق للقراءة

تحويل JSON إلى Python: dataclass وPydantic وTypedDict (دليل 2026)

الصق الـ JSON في محوّل JSON إلى Python وانسخ الفئات التي يولّدها. هذا هو المسار السريع لتحويل JSON إلى Python: لا شيء لتثبيته، ولا شيء يُرفع، وكل شيء يجري داخل متصفحك. لنموذج لمرة واحدة أو نظرة سريعة على استجابة API غير مألوفة، تنتهي خلال ثوانٍ.

المشكلة الأصعب هي تلك التي لا يستطيع المحوّل حلّها نيابةً عنك. فهو يستنتج الأنواع الصحيحة، لكنه لا يستطيع اتخاذ القرار الأهم: أيّ المخرجات الثلاثة تريده فعلًا. يمكن لِـ dataclass، ولِـ BaseModel من Pydantic v2، ولِـ TypedDict أن تصف الـ JSON نفسه تمامًا، ومع ذلك تختلف في قوة أمان الأنواع، وفي السلوك أثناء التشغيل، وفي كيفية تحميل البيانات إليها. اختر الخيار الخاطئ، فإمّا أن تحمل تبعية Pydantic بلا فائدة، أو تفترض أن الـ dataclass يتحقق من مدخلاتك بينما هو لا يفعل ذلك بصمت.

يغطّي هذا الدليل مصفوفة القرار للأنواع الثلاثة جميعها، وكيف يستنتج المحوّل الأنواع، ومعالجة camelCase في كل وضع، وكيفية تحليل الـ JSON باستخدام الفئة التي تولّدها (بما في ذلك فخّ التداخل الذي يوقع كثيرين)، وPydantic v2 مقابل v1، والحالات الحدّية التي يستحق معرفتها قبل أن تثق بالمخرجات.

كيفية تحويل JSON إلى Python

يستغرق تحويل JSON إلى Python ثلاث خطوات:

  1. الصق الـ JSON. أسقِط كائنًا أو مصفوفة أو استجابة API خامًا في صندوق الإدخال. يجري التحويل فورًا وبالكامل من جانب العميل (client-side).
  2. اختر dataclass أو Pydantic أو TypedDict. استخدم مبدّل المخرجات للتبديل بين الأنماط، وأعد تسمية Root الافتراضي إلى اسم ذي معنى مثل User أو ApiResponse.
  3. انسخ أو نزّل. احصل على شفرة Python المولّدة بنقرة واحدة وأسقِط models.py مباشرةً في مشروعك.

هذا هو المسار العملي بالكامل. إذا كان مدخلك مضغوطًا (minified) أو لست متأكدًا من صحته، مرّره أولًا عبر منسّق JSON كي يحصل المحوّل على JSON نظيف وسليم البنية ليقرأه. تشرح بقية هذا الدليل المخرجات كي تتمكن من إصلاح الحالات التي لا تستطيع الأداة استنتاجها بمفردها.

كيف تُطابِق أنواع JSON أنواع Python

لكل قيمة JSON نظير في Python، والمطابقة مباشرة:

قيمة JSONنوع Python
"text"str
42int (دقة غير محدودة، دون تجاوز)
3.14, 2e3float
true / falsebool
nullOptional[T] / Optional[Any]
[1, 2, 3]List[T]
{ ... }فئة مُسمّاة

خذ حمولة REST نموذجية:

{ "id": 101, "name": "Ada Lovelace", "email": "ada@example.com", "active": true, "roles": ["admin", "user"] }

في وضع dataclass، يُنتج المحوّل فئة جاهزة للاستخدام:

from dataclasses import dataclass
from typing import List


@dataclass
class Root:
    id: int
    name: str
    email: str
    active: bool
    roles: List[str]

ثمة فارق دقيق يميّز Python. فنوع int فيه ذو دقة غير محدودة، لذا يبقى معرّف snowflake من Discord أو أي معرّف يتجاوز 2^53 مجرد int عادي دون فقدان. أما JavaScript فسيفقد الدقة في القيمة نفسها، وRust سيجبرك على الاختيار بين i64 وu64 وf64. ولا يُستنتَج النوع float إلا لرمز مكتوب بفاصلة عشرية أو بأسّ. أما الحقل الذي لا يكون إلا null فلا يمكن تحديد نوعه من العيّنة وحدها، فيتراجع إلى Optional[Any]؛ الصق حمولة تمثيلية بقيمة مملوءة لتحصل على شيء أدق من Any.

dataclass مقابل Pydantic v2 مقابل TypedDict — أيّها تستخدم؟

هذا هو الخيار الذي يعيده إليك المحوّل، وهو جوهر هذا الدليل. باختصار: اختر dataclass حين تريد حاوية بيانات داخلية بلا أي تبعيات، و**Pydantic v2** حين تحلّل أو تتحقق من مدخلات غير موثوقة، و**TypedDict** حين تحتاج فقط إلى تلميحات أنواع ثابتة فوق قواميس تملكها أصلًا.

البُعدdataclassPydantic v2TypedDict
التبعيةالمكتبة القياسيةpip install pydanticالمكتبة القياسية (typing)
التحقق أثناء التشغيللانعم — يتحقق ويُكرِه الأنواعلا — تلميحات فقط
كلفة التشغيلخفيفة (كائن حقيقي)بعضها (التحقق)صفر (هو dict)
التحليل التلقائي للمتداخللا، يدوينعم، model_validate يتعمّق تعاوديًالا، هو dict
أسماء camelCase البديلةيُبقي المفتاح الأصليField(alias=...)صيغة دالّية
الأنسب لـالبُنى الداخليةاستجابات API وwebhooks والإعداداتإضافة أنواع لشفرة قواميس قديمة

تبدو الأهداف الثلاثة متطابقة تقريبًا لبنية صغيرة مثل {"id": 101, "name": "Ada Lovelace", "active": true}. يمنحك تحويل JSON إلى dataclass في Python كائنًا من المكتبة القياسية:

from dataclasses import dataclass


@dataclass
class Root:
    id: int
    name: str
    active: bool

أما نسخة JSON إلى Pydantic فتستبدل BaseModel، الذي يبدو مشابهًا لكنه يبرّر وجوده أثناء التشغيل:

from pydantic import BaseModel


class Root(BaseModel):
    id: int
    name: str
    active: bool

وTypedDict في Python المولّد من JSON يصف بنية dict عادي دون تخصيص أي شيء جديد:

from typing import TypedDict


class Root(TypedDict):
    id: int
    name: str
    active: bool

الحقول نفسها، لكن ثلاثة عقود مختلفة تمامًا. قيمة توليد الأنواع الثلاثة من عيّنة واحدة أنك تستطيع التبديل بينها في الأداة ومطابقة الوضع للمهمة: Pydantic حين تحلّل بيانات لا تتحكم بها، وdataclass حين تكون البيانات داخلية، وTypedDict حين تريد فقط أن يفهم mypy قاموسًا تمرّره أصلًا.

كيف يستنتج المحوّل الفئات

ثلاث قواعد تغطي كل ما تُطعمه للمحوّل تقريبًا: فئة واحدة لكل بنية كائن، ودمج مفتاحًا بمفتاح للمصفوفات، ومعالجة دقيقة للقيم الغائبة.

الاستنتاج البنيوي: فئة مُسمّاة واحدة لكل كائن

تصبح كل بنية كائن مميزة فئة مُسمّاة خاصة بها. الكائنات المتداخلة لا تُدمَج داخليًا؛ بل تُرفَع إلى تعريفات منفصلة يُشار إليها:

{ "repo": "pydantic", "owner": { "login": "samuelcolvin", "id": 100 } }
from dataclasses import dataclass


@dataclass
class Owner:
    login: str
    id: int


@dataclass
class Root:
    repo: str
    owner: Owner

يصبح owner المتداخل فئة Owner خاصة به، يُشار إليها عبر الحقل. لاحظ الترتيب: تُطبَع الفئة الابن قبل الأب الذي يستخدمها، فيعمل الملف دون from __future__ import annotations. كما تُزال البنى المتطابقة المكررة، فيتشارك حقلان لهما البنية نفسها فئة واحدة بدلًا من إنتاج نسخ.

دمج المصفوفات والحقول Optional

حين تمرّر مصفوفة كائنات، يدمجها المحوّل مفتاحًا بمفتاح في نوع عنصر واحد. المفتاح الموجود في بعض العناصر والغائب من أخرى يصبح Optional:

{ "users": [{ "id": 1, "nick": "x" }, { "id": 2 }] }
from typing import List, Optional, TypedDict


class User(TypedDict):
    id: int
    nick: Optional[str]


class Root(TypedDict):
    users: List[User]

يظهر id في كل عنصر، فيبقى مطلوبًا. أما nick فغائب عن المستخدم الثاني، فيصبح Optional[str]. لهذا السبب بالضبط تكون العيّنة المكوّنة من كائن واحد ضعيفة: لا يمكن للأداة أن تعلّم حقلًا بأنه Optional إلا حين ترى المفتاح غائبًا فعلًا في مكان ما. مرّر مصفوفة تمثيلية فيعكس الاستنتاج البنية الحقيقية.

Optional وNone والمصفوفات الفارغة

بعض القيم لا تحمل نوعًا بذاتها، والمحوّل صادق حيال ذلك. المفتاح الغائب من بعض عناصر المصفوفة يصبح Optional[T]. والحقل الذي لا يكون إلا null يصبح Optional[Any]، لأن null في JSON وحده لا يخبرك بشيء عن النوع المقصود. والمصفوفة الفارغة أو التي تحتوي عناصر مختلطة الأنواع تتراجع إلى List[Any]. لا شيء من هذا خطأ برمجي؛ بل هو امتناع الأداة عن التخمين. استبدل كل Any بنوع محدد بمجرد أن تحصل على عيّنة تُظهره، وتذكّر أن int في Python ذا الدقة غير المحدودة يعني أن معرّفًا كبيرًا لن يجبرك أبدًا على نوع عددي أوسع كما يحدث في Rust أو JavaScript.

معالجة مفاتيح camelCase في كل وضع

غالبًا ما تكون مفاتيح JSON بصيغة camelCase بينما حقول Python الاصطلاحية تكون snake_case. لا توجد إجابة واحدة صحيحة هنا، فيحلّ كل وضع الأمر بطريقة مختلفة، والفرق يستحق الفهم قبل أن تنسخ المخرجات.

dataclass. لا تملك الـ dataclass آلية أسماء بديلة مدمجة. وللإبقاء على عمل Root(**data)، يُبقي المحوّل المفتاح الأصلي اسمًا للحقل ما دام معرّف Python صالحًا، فيبقى publicRepos هو publicRepos. وإجباره على snake_case سيكسر **data بخطأ TypeError.

Pydantic v2. هنا تحصل على النتيجة الاصطلاحية. تُعاد تسمية الحقول إلى snake_case مع Field(alias=...) يربطها بمفتاح JSON الدقيق، إضافةً إلى model_config = ConfigDict(populate_by_name=True) كي تتمكن من بناء النموذج باسم الحقل أو بالاسم البديل:

from pydantic import BaseModel, ConfigDict, Field


class Root(BaseModel):
    model_config = ConfigDict(populate_by_name=True)

    login: str
    public_repos: int = Field(alias="publicRepos")
    created_at: str = Field(alias="createdAt")

هذا ما يتيح لِـ Root.model_validate(api_json) أن يتعامل ذهابًا وإيابًا مع حمولات camelCase الحقيقية بينما تبقى شفرتك مقروءة بشكل طبيعي.

TypedDict. تُستخدم المفاتيح ذات المعرّفات العادية مباشرةً. وبمجرد ظهور مفتاح ليس معرّفًا صالحًا، يتحوّل المحوّل إلى الصيغة الدالّية TypedDict('User', {...}) للحفاظ على المفتاح الدقيق، وهو ما يتناوله القسم التالي.

كلمات Python المحجوزة والمفاتيح غير المعرّفة

مفاتيح JSON مجرد نصوص، فلا شيء يمنع API من إرسال class أو from أو first-name. وكلٌّ منها سيكون خطأً نحويًا كسمة Python مجردة، لذا يُنقّيها المحوّل.

المفتاح الذي يكون كلمة Python محجوزة يحصل على شرطة سفلية لاحقة: class يصبح class_، وfrom يصبح from_. وفي وضع Pydantic يحتفظ أيضًا بِـ Field(alias=...) كي يظل النموذج يقرأ مفتاح السلك الأصلي:

from pydantic import BaseModel, ConfigDict, Field


class Root(BaseModel):
    model_config = ConfigDict(populate_by_name=True)

    class_: str = Field(alias="class")
    from_: str = Field(alias="from")

المفاتيح التي تحتوي شرطات أو مسافات أو رقمًا في البداية تُنقّى إلى معرّفات صالحة في وضعَي dataclass وPydantic. وTypedDict هو الوضع الوحيد الذي يستطيع الإبقاء على المفتاح الحرفي، مستخدمًا الصيغة الدالّية بحيث يبقى "first-name" دون مساس:

from typing import TypedDict

Root = TypedDict("Root", {"first-name": str, "2fa": bool})

تعمل النتيجة دائمًا، وفي وضع TypedDict يبقى المفتاح بايتًا ببايت كما أرسله الـ API.

تحليل JSON باستخدام الفئة المولّدة

توليد الفئة نصف المهمة. تحميل الـ JSON إليها هو حيث تتباعد الأوضاع الثلاثة بحدّة، وحيث يخطئ افتراض شائع.

dataclass. لكائن مسطّح، يعمل json.loads مع Root(**data) مباشرةً:

import json
from dataclasses import dataclass
from typing import List


@dataclass
class Root:
    id: int
    name: str
    roles: List[str]


data = json.loads('{"id": 101, "name": "Ada Lovelace", "roles": ["admin"]}')
root = Root(**data)
print(root.name, root.roles)

لكن الـ dataclass لا يتعمّق تعاوديًا. إذا احتوى الـ JSON كائنات متداخلة، فإن Root(**data) يملأ المستوى الأعلى ويترك كل ابن dict عاديًا، لا فئة Owner التي ولّدتها. ولتحليل الشجرة كاملةً، ابنِ الأبناء بنفسك، أو استخدم مكتبة مثل dacite أو pydantic.dataclasses، أو بدّل الأداة إلى وضع Pydantic. هذا هو المزلق الذي لا تذكره معظم المحوّلات.

Pydantic v2. استدعاء واحد يحلّل ويتحقق من الشجرة كاملةً:

import json
from pydantic import BaseModel


class Root(BaseModel):
    id: int
    name: str


root = Root.model_validate(json.loads('{"id": 101, "name": "Ada Lovelace"}'))
print(root.name)

عدم تطابق النوع يرفع ValidationError واضحًا بدلًا من المرور بصمت. هذا هو مردود التبعية: النموذج مُتحقِّق أثناء التشغيل، فاستدعاء model_validate الذي يعود بنتيجة هو ضمان بأن البيانات طابقت، لا مجرد اعتقاد من مدقّق أنواع بأنها فعلت.

TypedDict. أثناء التشغيل، الـ TypedDict هو dict عادي. وتعليق النوع data: Root = json.loads(text) هو للمدقّق الثابت فقط؛ ولا يُتحقَّق من شيء حين تعمل الشفرة. إن احتجت إنفاذًا حقيقيًا، شغّل mypy في الـ CI أو انتقل إلى Pydantic. اختيار الوضع هو اختيار مقدار الضمان الذي تريده أثناء التشغيل، وحين يحتاج العقد إلى ما يتجاوز الأنواع، يغطي دليل التحقق من JSON Schema إنفاذ البنية من طرف إلى طرف.

Pydantic v2 مقابل v1 — ما الذي تغيّر

يُصدر المحوّل صيغة v2. لا تزال قواعد شفرات كثيرة تعمل بِـ v1، ولصق مخرجات v2 في مشروع v1 يفشل بسبب أساليب أُعيدت تسميتها لا بسبب المنطق. إليك المطابقة:

المهمةPydantic v2 (مخرجات الأداة)Pydantic v1
تحليل dictmodel_validate(data)parse_obj(data)
تحليل JSON خامmodel_validate_json(text)parse_raw(text)
ضبط النموذجmodel_config = ConfigDict(...)class Config داخلية
التسلسل إلى dictmodel_dump()dict()
التسلسل إلى JSONmodel_dump_json()json()

كما تشدّد سلوك الأسماء البديلة: في v2 تحتاج populate_by_name=True إلى جانب Field(alias=...) لبناء نموذج باسم الحقل أو بالاسم البديل، ولهذا تُضمّنها الأداة. القاعدة العملية قصيرة. رقِّ إلى v2 إن استطعت، فهو أسرع وهو هدف التطوير الحالي؛ وإلا فأعد كتابة model_validate إلى parse_obj وConfigDict إلى Config داخلية.

json-to-python مقابل quicktype مقابل datamodel-code-generator مقابل الكتابة اليدوية

لا توجد طريقة واحدة مثلى لتوليد فئة Python من JSON. يعتمد الأمر على أين يوجد الـ JSON وما الذي تبدأ منه.

الأسلوبالأنسب لـملاحظة
محوّل عبر الإنترنت (هذه الأداة، jsonlint، codeshack)التحويلات لمرة واحدة، الحمولات الحساسة، بلا تثبيتيستنتج من عيّنة، بالكامل من جانب العميل
quicktypeمخرجات متعددة اللغات، توليد شفرة ضمن خط أنابيبمدفوع بالعيّنات أيضًا؛ دعم Python أضعف في الأسماء البديلة واختيار الوضع
datamodel-code-generatorتوليد Pydantic من مخطط JSON أو OpenAPIالمدخل مخطط لا عيّنة — أكثر موثوقية حين تملك واحدًا
كتابة الفئات يدويًاالحمولات الصغيرة، تعلّم الصيغةتحكّم كامل، لكنه ممل وسهل الانحراف

الفرق الحاسم بين الأسلوبين: المحوّل عبر الإنترنت وquicktype كلاهما يستنتج الأنواع من عيّنة من الـ JSON، بينما يقرأ datamodel-code-generator مواصفة JSON Schema أو OpenAPI بوصفها مصدر الحقيقة. استخدم أدوات العيّنات للاستكشاف والحالات العابرة؛ ولجأ إلى المدفوعة بالمخطط حين يكون هناك عقد قائم أصلًا. إن كانت قاعدة شفرتك بلغة TypeScript بدلًا من Python، فينطبق الأسلوب نفسه المدفوع بالعيّنات مع محوّل JSON إلى TypeScript، ويغطي دليل واجهات JSON إلى TypeScript ذلك الجانب بعمق. ولزاوية التحقق أثناء التشغيل في لغة مُصرَّفة قوية الأنواع، يقدّم دليل بُنى JSON إلى Rust مقارنة مفيدة: فَـ serde هو Pydantic الخاص بِـ Rust.

مزالق شائعة عند توليد Python من JSON

الفئات المولّدة نقطة انطلاق. انتبه لهذه قبل أن تعتمد على المخرجات مع بيانات حيّة.

  • نادرًا ما تكشف عيّنة واحدة كل بنية. لا يمكن استنتاج Optional إلا من عناصر مصفوفة تختلف فعلًا. الصق مصفوفة تمثيلية كي تكون الاختيارية دقيقة، لا تخمينًا من كائن محظوظ واحد.
  • الـ dataclass لا يتحقق. Root(**data) يسند الحقول فقط؛ لا يفحص أي أنواع ولا يتعمّق في شيء. إن احتجت إنفاذًا، استخدم Pydantic.
  • الـ JSON المتداخل يكسر Root(**data). يُملأ المستوى الأعلى فقط؛ ويبقى الأبناء قواميس. انتقل إلى model_validate الخاص بِـ Pydantic، أو استخدم dacite.
  • الحقل الذي يكون دائمًا null يصبح Optional[Any]. عامله كعنصر نائب لا كحقل اختياري عادي، وامنحه نوعًا حقيقيًا بمجرد أن تعرف واحدًا.
  • مخرجات v2 في مشروع v1. سيكون model_validate غير مُعرَّف. رقِّ إلى v2 أو أعد كتابته إلى parse_obj.
  • التاريخ المتروك كـ str لن يجري حساب تواريخ. حوّل الحقل إلى datetime، أو في Pydantic استخدم حقل datetime كي يحلّل ISO 8601 نيابةً عنك.

الأسئلة الشائعة

هل ينبغي أن أستخدم dataclass أم Pydantic أم TypedDict للـ JSON؟

استخدم dataclass لحاوية بيانات داخلية بلا تبعيات. واستخدم Pydantic v2 حين تحلّل أو تتحقق من مدخلات غير موثوقة، لأنه يُنفِذ الأنواع أثناء التشغيل. واستخدم TypedDict لإضافة تلميحات ثابتة لقاموس تملكه أصلًا، بلا كلفة أثناء التشغيل. وحين تأتي البيانات من خارج سيطرتك، اجعل Pydantic خيارك الافتراضي.

هل يتحقق Pydantic من JSON أثناء التشغيل؟

نعم. Root.model_validate(data) يتحقق ويُكرِه الأنواع أثناء تشغيل الشفرة، رافعًا ValidationError عند المدخل السيئ. الـ dataclass لا يفعل أيًّا منهما — Root(**data) يسند الحقول فقط. وTypedDict لا يفعل أيًّا منهما أيضًا؛ فهو تلميح للمدقّقات الثابتة مثل mypy ويتصرف كقاموس عادي أثناء التشغيل.

كيف أحدد نوع حقل يحذفه JSON أحيانًا؟

حدّد نوعه كـ Optional[T]. حين يظهر مفتاح في بعض عناصر المصفوفة فقط، يعلّمه المحوّل Optional تلقائيًا. وفي Pydantic، امنح الحقل قيمة افتراضية مثل = None كي لا يُطلق المفتاح الغائب خطأ تحقق. ويمكن لحقل dataclass أن يأخذ قيمة افتراضية بالطريقة نفسها عبر field(default=None).

لماذا فشل الـ dataclass في تحليل JSON المتداخل؟

لأن Root(**data) لا يتعمّق تعاوديًا. فهو يملأ حقول المستوى الأعلى ويترك الكائنات المتداخلة قواميس عادية بدلًا من الفئات الابن التي ولّدتها. ولتحليل الشجرة كاملةً في استدعاء واحد، انتقل إلى model_validate الخاص بِـ Pydantic، أو أضف التعاود عبر dacite أو pydantic.dataclasses.

إلى أي نوع Python يتحول عدد JSON صحيح كبير؟

int. أعداد Python الصحيحة ذات دقة غير محدودة، لذا يبقى معرّف snowflake أو معرّف Discord يتجاوز 2^53 مجرد int عادي دون فقدان. لا تصطدم أبدًا بفقدان الدقة الموجود في JavaScript، ولا تضطر أبدًا إلى الاختيار بين i64 وu64 كما يجبرك Rust.

كيف تُعالَج مفاتيح JSON بصيغة camelCase في كل وضع؟

الـ dataclass يُبقي المفتاح الأصلي كي يظل Root(**data) يعمل. وPydantic يعيد تسمية الحقول إلى snake_case مع Field(alias="camelKey") إضافةً إلى populate_by_name=True، فيتعامل ذهابًا وإيابًا مع بيانات API الحقيقية. وTypedDict يستخدم الصيغة الدالّية TypedDict('Name', {...}) للحفاظ على أي مفتاح ليس معرّفًا صالحًا.

كيف أتعامل مع مفتاح JSON يكون كلمة Python محجوزة مثل class؟

يُلحق المحوّل شرطة سفلية لاحقة، فيحوّل class إلى class_ كي تكون الشفرة صالحة. وفي وضع Pydantic يضيف أيضًا Field(alias="class") يربط الحقل بمفتاح السلك الأصلي، فيكون الاسم قانونيًا ويظل النموذج يقرأ الحمولة الحقيقية.

هل يبقى JSON الخاص بي خاصًا عند استخدام محوّل JSON إلى Python عبر الإنترنت؟

نعم. يجري التحويل بنسبة 100% داخل متصفحك باستخدام JavaScript. الـ JSON الخاص بك — بما في ذلك الرموز المميزة والمعرّفات وبيانات العملاء — لا يغادر الصفحة أبدًا ولا يُرسل إلى خادم أبدًا. بل يعمل حتى دون اتصال بمجرد تحميل الصفحة. وحين تكون جاهزًا، جرّب محوّل JSON إلى Python على حمولتك الخاصة.

الوسوم: python json pydantic type-safety developer-tools

مقالات ذات صلة

عرض جميع المقالات