JSON إلى Dart

توليد كلاسات Dart من JSON مع fromJson وtoJson

جرب مثالاً
اسم الكلاس الجذري:

إدخال JSON

مخرجات Dart

يعمل محليًا · آمن للصق الأسرار
ستظهر كلاسات Dart هنا…

ما هو تحويل JSON إلى Dart؟

يأخذ تحويل JSON إلى Dart كائن JSON خامًا وينتج تعريفات كلاسات Dart بحقول محددة الأنواع ومُنشئ بمعاملات مسمّاة ومصنع fromJson وتابع toJson. لا يدعم Flutter الانعكاس في وقت التشغيل (dart:mirrors معطّل)، لذا لا يمكن تحويل JSON إلى كائنات محددة الأنواع دون كتابة كود تعيين صريح. كل رد من API أو مستند Firebase أو حمولة إعداد يحتاج إلى كلاس نموذج Dart مقابل قبل أن تتمكن من الوصول إلى حقوله بطريقة آمنة الأنواع.

يُعرّف كلاس نموذج Dart القياسي لـJSON حقولًا final لكل مفتاح، ومُنشئًا بمعاملات مسمّاة (يستخدم الكلمة المفتاحية required للحقول غير القابلة للإلغاء)، ومُنشئ مصنع باسم fromJson يقرأ من Map<String, dynamic>، وتابع toJson يُعيد Map<String, dynamic>. تصبح كائنات JSON المتداخلة كلاسات منفصلة. تصبح المصفوفات حقول List موصوفة بالأنواع. تستخدم قيم JSON القابلة للإلغاء صيغة سلامة null في Dart مع اللاحقة ? على النوع.

كتابة كلاسات النماذج هذه يدويًا يعني قراءة كل مفتاح JSON وتحديد نوع Dart المناسب وإنشاء تحويل fromJson لكل حقل (بما في ذلك تعيين القوائم باستخدام .map().toList()) وبناء literal لخريطة toJson وتكرار ذلك لكل كائن متداخل. لكائن JSON بـ12 حقلًا وكائنَين متداخلَين، هذا يعني 3 كلاسات و6 أسطر مصنع وعشرات من تعبيرات التحويل. يُنتج المحوّل كل هذا في أجزاء من الثانية من لصقة واحدة.

لماذا تستخدم محوّل JSON إلى Dart؟

كتابة كلاسات نموذج Dart يدويًا من JSON تعني قراءة أسماء الحقول وتخمين الأنواع من القيم النموذجية وكتابة تحويلات fromJson مع معالجة صحيحة للقيم الفارغة وتكرار العملية للكائنات المتداخلة. عند تغيّر شكل الـAPI، كل تحديث للحقل يلمس المُنشئ وfromJson وtoJson. يُزيل المحوّل هذا العمل المتكرر.

توليد فوري للكلاسات
الصق JSON واحصل على كلاسات Dart كاملة مع مُنشئات ومصانع fromJson وتوابع toJson في أقل من ثانية. تُكتشف الكائنات المتداخلة والقوائم وتُعيَّن تلقائيًا.
🔒
المعالجة محلية — بياناتك خاصة
يعمل التحويل بالكامل في متصفحك باستخدام JavaScript. لا يغادر JSON لديك جهازك. تبقى مفاتيح الـAPI والرموز المميزة وبيانات الإنتاج خاصة.
📝
مخرجات آمنة من null
تستخدم الكلاسات المولَّدة Null Safety الصارمة في Dart. تحصل حقول JSON القابلة للإلغاء على اللاحقة ? في نوعها وتُحذف منها الكلمة المفتاحية required في المُنشئات. الحقول غير القابلة للإلغاء تُعلَّم بـrequired.
📦
بدون تثبيت أو تسجيل
افتح الصفحة والصق JSON. لا يتطلب Dart SDK، ولا تبعيات pub، ولا إنشاء حساب. يعمل على أي جهاز بمتصفح.

حالات استخدام تحويل JSON إلى Dart

تطوير تطبيقات Flutter
ولّد كلاسات نماذج لردود API المُستهلَكة بحزمتَي http أو dio. الصق JSON المُعاد من الخلفية واحصل على كلاسات Dart جاهزة لـjsonDecode وعرض الواجهة.
تكامل Firebase وFirestore
أنشئ كلاسات نماذج موصوفة بالأنواع لقراءات مستندات Firestore. الصق مستندًا نموذجيًا بصيغة JSON وولّد كلاس Dart واستخدم fromJson مع snapshot.data() في طبقة المستودع.
نماذج إدارة الحالة
عرّف كائنات حالة محددة الأنواع لـRiverpod أو Bloc أو Provider. الصق شكل الحالة المتوقع بصيغة JSON واحصل على كلاسات Dart غير قابلة للتعديل مع toJson لحفظ الحالة واستعادتها.
توليد كود عميل الـAPI
أنتج نماذج الطلبات والردود لعملاء API من Retrofit أو Chopper. الصق حمولات JSON نموذجية واحصل على كلاسات Dart تطابق مخططات ردود OpenAPI.
الاختبار الآلي
ابنِ بيانات اختبار موصوفة بالأنواع من ردود API حقيقية. الصق نموذج JSON وولّد كلاس النموذج واستخدمه مباشرة في اختبارات الوحدة والواجهة دون كتابة هياكل البيانات يدويًا.
تعلّم أنماط Dart
يستطيع الطلاب المبتدئون في Flutter لصق أي بنية JSON ومشاهدة كيف تُمثّلها Dart: الحقول final والمُنشئات بأسماء معاملات ومصانع fromJson وتعليقات سلامة null في التطبيق العملي.

جدول تعيين أنواع JSON إلى Dart

كل قيمة JSON تتعيّن على نوع Dart محدد. يوضح الجدول أدناه كيف يُترجم المحوّل كل نوع JSON. يعرض عمود البديل الأنواع المستخدمة في سيناريوهات التعيين اليدوي أو الأقل شيوعًا.

نوع JSONمثالنوع Dartالبديل
string"hello"StringString
number (integer)42intint
number (float)3.14doubledouble
booleantrueboolbool
nullnulldynamicNull / dynamic
object{"k": "v"}NestedClassMap<String, dynamic>
array of strings["a", "b"]List<String>List<String>
array of objects[{"id": 1}]List<Item>List<Item>
mixed array[1, "a"]List<dynamic>List<dynamic>

أساليب تحليل JSON في Dart

تقدم Dart وFlutter عدة طرق للتعامل مع تحليل JSON. يُعدّ fromJson/toJson اليدوي أبسطها ولا يحتاج إلى توليد كود. للمشاريع الأكبر، تولّد الحلول القائمة على build_runner مثل json_serializable وfreezes الشيفرة المتكررة وقت التصريف، مما يُقلل الأخطاء ويُبقي النماذج متسقة مع عقد JSON.

الأسلوبالوصفالمصدر
json_serializableCode-generation-based JSON serialization. Generates .g.dart files at build time via build_runner. Type-safe and compile-time verified.Official (Google)
freezedGenerates immutable data classes with copyWith, fromJson/toJson, equality, and pattern matching support. Built on top of json_serializable.Community (rrousselGit)
built_valueGenerates immutable value types with serialization. Enforces immutability patterns. Used in larger codebases with strict data modeling.Google
dart_mappableAnnotation-based mapper that generates fromJson, toJson, copyWith, and equality. Simpler setup than freezed with similar features.Community
Manual fromJson/toJsonHand-written factory constructors and toJson methods. No code generation needed. Full control over the mapping logic.Built-in Dart

يدوي مقابل json_serializable مقابل freezed

تتوفر في Dart ثلاثة أساليب شائعة لكلاسات نماذج JSON. اليدوي باستخدام fromJson/toJson لا يحتاج إلى أي تبعيات. json_serializable يُؤتمت كود التعيين. freezed يضيف الثبات وcopyWith ومطابقة الأنماط فوق json_serializable.

Manual fromJson/toJson
كتابة مصانع fromJson وتوابع toJson يدويًا. لا تبعيات، ولا خطوة build_runner. تتحكم بالكامل في منطق التحويل ومعالجة الأخطاء. الأنسب للمشاريع الصغيرة ذات النماذج القليلة، أو عند الحاجة إلى تحليل مخصص لا تستطيع مولّدات الكود التعبير عنه. هذا هو تنسيق المخرجات الذي تولّده هذه الأداة.
json_serializable
علّق كلاسك بـ@JsonSerializable() ونفّذ dart run build_runner build. يُنشئ المولّد ملف .g.dart يحتوي على دوال _$ClassFromJson و_$ClassToJson. يدعم @JsonKey لإعادة تسمية الحقول والقيم الافتراضية والمحوّلات المخصصة. الخيار المعياري للمشاريع المتوسطة والكبيرة في Flutter.
freezed
علّق بـ@freezed واحصل على كلاسات غير قابلة للتعديل مع fromJson وtoJson وcopyWith والمساواة (== وhashCode) وtoString المولَّدة تلقائيًا. يدعم أنواع الاتحاد لأنماط الكلاسات المختومة. يتطلب كلًّا من freezed وjson_serializable كتبعيات للتطوير. الأنسب لنماذج إدارة الحالة وكائنات المجال المعقدة.

أمثلة كود

تُوضح هذه الأمثلة كيفية استخدام كلاسات Dart المولَّدة لتحليل JSON، وكيفية إعداد json_serializable مع build_runner، وكيفية توليد كلاسات Dart برمجيًا من JavaScript وPython.

Dart (manual fromJson / toJson)
import 'dart:convert';

class User {
  final int id;
  final String name;
  final String email;
  final bool active;
  final Address address;
  final List<String> tags;

  User({
    required this.id,
    required this.name,
    required this.email,
    required this.active,
    required this.address,
    required this.tags,
  });

  factory User.fromJson(Map<String, dynamic> json) => User(
    id: json['id'] as int,
    name: json['name'] as String,
    email: json['email'] as String,
    active: json['active'] as bool,
    address: Address.fromJson(json['address'] as Map<String, dynamic>),
    tags: (json['tags'] as List<dynamic>).map((e) => e as String).toList(),
  );

  Map<String, dynamic> toJson() => {
    'id': id,
    'name': name,
    'email': email,
    'active': active,
    'address': address.toJson(),
    'tags': tags,
  };
}

class Address {
  final String street;
  final String city;
  final String zip;

  Address({required this.street, required this.city, required this.zip});

  factory Address.fromJson(Map<String, dynamic> json) => Address(
    street: json['street'] as String,
    city: json['city'] as String,
    zip: json['zip'] as String,
  );

  Map<String, dynamic> toJson() => {'street': street, 'city': city, 'zip': zip};
}

void main() {
  final jsonStr = '{"id":1,"name":"Alice","email":"alice@example.com","active":true,"address":{"street":"123 Main","city":"Springfield","zip":"12345"},"tags":["admin","user"]}';
  final user = User.fromJson(jsonDecode(jsonStr));
  print(user.name); // -> Alice
  print(jsonEncode(user.toJson())); // -> round-trip back to JSON
}
Dart (json_serializable + build_runner)
// pubspec.yaml dependencies:
//   json_annotation: ^4.8.0
//   dev_dependencies:
//     build_runner: ^2.4.0
//     json_serializable: ^6.7.0

import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart'; // generated by: dart run build_runner build

@JsonSerializable()
class User {
  final int id;
  final String name;
  final String email;
  @JsonKey(name: 'is_active')
  final bool isActive;
  final List<String> tags;

  User({
    required this.id,
    required this.name,
    required this.email,
    required this.isActive,
    required this.tags,
  });

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

// Run code generation:
// dart run build_runner build --delete-conflicting-outputs
JavaScript (generate Dart classes from JSON)
function jsonToDart(obj, name = "Root") {
  const classes = [];
  function infer(val, fieldName) {
    if (val === null) return "dynamic";
    if (typeof val === "string") return "String";
    if (typeof val === "number") return Number.isInteger(val) ? "int" : "double";
    if (typeof val === "boolean") return "bool";
    if (Array.isArray(val)) {
      const first = val.find(v => v !== null);
      if (!first) return "List<dynamic>";
      return `List<${infer(first, fieldName)}>`;
    }
    if (typeof val === "object") {
      const cls = fieldName.charAt(0).toUpperCase() + fieldName.slice(1);
      build(val, cls);
      return cls;
    }
    return "dynamic";
  }
  function build(obj, cls) {
    const fields = Object.entries(obj).map(([k, v]) =>
      `  final ${infer(v, k)} ${k};`
    );
    classes.push(`class ${cls} {\n${fields.join("\n")}\n}`);
  }
  build(obj, name);
  return classes.join("\n\n");
}

console.log(jsonToDart({ id: 1, name: "Alice", scores: [98, 85] }, "User"));
// class User {
//   final int id;
//   final String name;
//   final List<int> scores;
// }
Python (generate Dart model from JSON)
import json

def json_to_dart(obj: dict, class_name: str = "Root") -> str:
    classes = []

    def infer(val, name):
        if val is None:
            return "dynamic"
        if isinstance(val, bool):
            return "bool"
        if isinstance(val, int):
            return "int"
        if isinstance(val, float):
            return "double"
        if isinstance(val, str):
            return "String"
        if isinstance(val, list):
            if not val:
                return "List<dynamic>"
            return f"List<{infer(val[0], name)}>"
        if isinstance(val, dict):
            cls = name[0].upper() + name[1:]
            build(val, cls)
            return cls
        return "dynamic"

    def build(obj, cls):
        fields = [f"  final {infer(v, k)} {k};" for k, v in obj.items()]
        classes.append(f"class {cls} {{\n" + "\n".join(fields) + "\n}")

    build(obj, class_name)
    return "\n\n".join(classes)

data = json.loads('{"id": 1, "name": "Alice", "active": true}')
print(json_to_dart(data, "User"))
# class User {
#   final int id;
#   final String name;
#   final bool active;
# }

أسئلة شائعة

لماذا لا يمكن لـFlutter استخدام dart:mirrors لتحليل JSON؟
تُعطّل Flutter مكتبة dart:mirrors (مكتبة الانعكاس) لأن tree-shaking يتطلب معرفة الكود المستخدم وقت التصريف. سيمنع الانعكاس المُصرّف من حذف الكلاسات غير المستخدمة، مما يزيد حجم التطبيق. لهذا تحتاج مشاريع Flutter إلى توابع fromJson/toJson صريحة أو توليد كود بدلًا من الانعكاس في وقت التشغيل.
هل يجب استخدام json_serializable أم كتابة fromJson يدويًا؟
للمشاريع التي تحتوي على أقل من 10 كلاسات نماذج، يُعدّ fromJson/toJson المكتوب يدويًا مباشرًا ولا يضيف تبعيات بناء. بمجرد تجاوز 10 نماذج أو كثرة التغييرات، يوفّر json_serializable الوقت ويُقلل أخطاء النسخ. يولّد كود التعيين من التعليقات، فإضافة حقل لا تتطلب سوى إعادة تشغيل build_runner.
كيف يتعامل المحوّل مع كائنات JSON المتداخلة؟
يصبح كل كائن JSON متداخل كلاسًا Dart منفصلًا. إذا احتوى حقل JSON باسم "address" على كائن بمفتاحَي "street" و"city"، يُنشئ المحوّل كلاس Address بهذه الحقول ويُصنّف الحقل الأب بنوع Address. يُحوّل مصنع fromJson الخريطة المتداخلة ويُمررها إلى Address.fromJson.
ماذا يحدث عندما يكون حقل JSON بقيمة null؟
تأخذ الحقول الفارغة نوع dynamic مع اللاحقة ? لأن المحوّل لا يستطيع تحديد النوع المقصود من قيمة null وحدها. استبدل dynamic? بالنوع الصحيح (String? أو int? وما شابه) بمجرد معرفته من عقد الـAPI. ستُطبّق سلامة null في Dart حينئذٍ التحقق الصحيح من القيم الفارغة وقت التصريف.
كيف أتعامل مع مفاتيح JSON غير الصالحة كأسماء في Dart؟
مفاتيح JSON مثل "first-name" أو "2nd_place" ليست أسماء حقول Dart صالحة. مع fromJson اليدوي، يمكنك تعيين أي مفتاح JSON إلى أي اسم حقل Dart في مُنشئ المصنع. مع json_serializable، استخدم @JsonKey(name: 'first-name') للتعليق على الحقل. يُحوّل المحوّل تلقائيًا أسماء المفاتيح إلى حقول Dart بصيغة camelCase.
هل يمكنني استخدام freezed مع مخرجات هذه الأداة؟
تولّد الأداة كلاسات Dart قياسية مع fromJson/toJson يدوي. للتحويل إلى freezed، غيّر الكلاس ليستخدم تعليق @freezed، واحذف المُنشئ والمصنع المكتوبَين يدويًا، وأضف mixin الخاص بـfreezed. ثم نفّذ build_runner لتوليد ملفَي .freezed.dart و.g.dart. تنتقل أسماء الحقول وأنواعها من المخرجات المولَّدة مباشرة.
ما الفرق بين خريطة خام وكلاس Dart موصوف بالأنواع لتحليل JSON؟
تمنحك الخريطة الخام (Map<String, dynamic>) وصولًا مباشرًا لبيانات JSON دون التحقق من الأنواع. يجب تحويل كل قيمة عند نقطة الاستدعاء، والأخطاء المطبعية في أسماء المفاتيح تفشل صامتةً وقت التشغيل. يكتشف كلاس Dart الموصوف بالأنواع عدم تطابق الأنواع عند حدود fromJson، ويوفر إكمالًا تلقائيًا في بيئة التطوير لأسماء الحقول، ويجعل إعادة الهيكلة آمنة. الفرق في الأداء ضئيل؛ الفائدة كلها في الصحة وقابلية الصيانة.