Moshi: المفاهيم الأساسية، مكتبة JSON لـ Kotlin وكيف تعمل

المؤلف: IT Sectr نُشر: 2026-03-15 وقت القراءة: 8 دق

Moshi هي مكتبة JSON حديثة من Square، تم إنشاؤها خصيصاً لـ Kotlin و Android مع مراعاة قيود Gson. وهي متوافقة تماماً مع أمان القيم الفارغة في Kotlin، وتولد الكود في وقت الترجمة ولا تستخدم الانعكاس، مما يحسن الأداء والموثوقية. وفقاً لـ Square Moshi، 2024، توفر Moshi تسلسلاً يمكن التنبؤ به وتدعم المحولات المخصصة لأي نوع من البيانات.

النقاط الرئيسية

  • Moshi « مكتبة JSON من Square لـ Kotlin و Android بدون انعكاس
  • محول Kotlin « دعم مدمج لـ data class والقيم الافتراضية وأمان القيم الفارغة
  • @Json « شرح لتكوين اسم الحقل وتجاهل الخصائص
  • المحولات « منطق تسلسل مخصص عبر @ToJson و @FromJson
  • توليد الكود « Moshi تولد المحولات في وقت الترجمة عبر kapt أو KSP

ما هي Moshi

Moshi هي مكتبة JSON لـ JVM و Android و Kotlin Multiplatform، تم إنشاؤها بواسطة Square (مؤلفو OkHttp و Retrofit). على عكس Gson، لا تعتمد Moshi على الانعكاس « يتم توليد المحولات في وقت الترجمة عبر الشرح @JsonClass(generateAdapter = true). وهذا يجعل Moshi أسرع وأكثر أماناً وقابلية للتنبؤ عند العمل مع بنيات Kotlin الخاصة.

الفلسفة والمزايا

الفرق الرئيسي بين Moshi وسابقاتها هو التخلي عن الانعكاس. الانعكاس يسمح لـ Gson بالعمل مع أي فئة دون تحضير، لكن الثمن هو بطء التهيئة، وعدم إمكانية التحسين من قبل المترجم، وخطر الأخطاء في وقت التشغيل. تتطلب Moshi التصريح الصريح بالفئات لتوليد الكود، ولكن في المقابل توفر سرعة الكود المكتوب يدوياً وأماناً كاملاً للأنواع في وقت الترجمة.

kotlin
// إضافة Moshi إلى build.gradle
dependencies {
    implementation "com.squareup.moshi:moshi:1.15.0"
    implementation "com.squareup.moshi:moshi-kotlin:1.15.0"
    kapt "com.squareup.moshi:moshi-kotlin-codegen:1.15.0"
}

// نموذج بسيط مع توليد الكود
@JsonClass(generateAdapter = true)
data class User(
    @Json(name = "user_id")
    val id: Int,
    val name: String,
    val email: String,
    val avatar: String? = null
)

// الاستخدام
val moshi = Moshi.Builder()
    .build()
val jsonAdapter = moshi.adapter(User::class.java)

التثبيت والتكوين

للبدء في العمل مع Moshi، تحتاج إلى إضافة التبعيات في build.gradle وشرح النماذج. Moshi.Builder يعمل كنقطة دخول: من خلاله تتم إضافة المحولات المدمجة للأنواع القياسية والمحولات المخصصة وتكوين سلوك المكتبة. تدعم Moshi المحولات لـ Date و Enum و Collection و Map بشكل افتراضي، لكن فئات Kotlin تتطلب وحدة moshi-kotlin. على عكس Gson، لا تستخدم Moshi الانعكاس لفئات Kotlin افتراضياً « لهذا يتم توصيل KotlinJsonAdapterFactory، الذي يعمل كخيار احتياطي عندما لا يتم استخدام توليد الكود أو عندما لا تكون الفئة مشروحة بـ @JsonClass. يضمن هذا النهج أن المطور يختار بوضوح بين أداء توليد الكود ومرونة الانعكاس لكل فئة محددة.

إنشاء Moshi وإضافة المحولات

بعد بناء Moshi عبر Builder، يحصل المطور على مثيل Moshi ويطلب محولاً للفئة المطلوبة. JsonAdapter هو الكائن المركزي الذي يقوم بالتسلسل عبر toJson() وإلغاء التسلسل عبر fromJson(). تستخدم Moshi تلقائياً المحول المُولَّد إذا كانت الفئة مشروحة بـ @JsonClass(generateAdapter = true)، وإلا تطبق KotlinJsonAdapterFactory الانعكاسي كخيار احتياطي. يجمع هذا النهج بين سرعة توليد الكود ومرونة الآلية الانعكاسية للمشاريع من أي حجم وتعقيد. Moshi مناسبة لكل من التطبيقات الصغيرة والمشاريع المؤسسية الكبيرة التي تحتوي على مئات نماذج البيانات.

kotlin
// تكوين Moshi مع KotlinJsonAdapterFactory
val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    .add(LocalDateAdapter())
    .build()

// استخدام المحول
val adapter = moshi.adapter(User::class.java)

// التسلسل
val user = User(1, "Alice", "alice@test.com")
val json = adapter.toJson(user)

// إلغاء التسلسل
val jsonString = """{"user_id":2,"name":"Bob","email":"bob@test.com"}"""
val parsedUser = adapter.fromJson(jsonString)

// العمل مع القوائم
val listAdapter = moshi.adapter(
    Types.newParameterizedType(
        List::class.java,
        User::class.java
    )
)

الشروح والمحولات

Moshi تستخدم الشروح لتكوين التسلسل ودعم الأنواع المخصصة. @Json(name = "...") تحدد مفتاح JSON للحقل. @Transient تستبعد الحقل من التسلسل. @JsonClass(generateAdapter = true) تفعّل توليد الكود. للمنطق المخصص، توفر Moshi الشرحين @ToJson و @FromJson، يمكن وضعهما في فئة محول منفصلة.

@Json والمحولات المخصصة

الشرح @Json يستبدل @SerializedName من Gson ويعمل بشكل مشابه: الحقل kotlinName يرتبط بمفتاح JSON "kotlin_name". للأنواع التي لا تستطيع Moshi تسلسلها افتراضياً (مثل LocalDate)، يقوم المطور بإنشاء فئة بطريقتين @ToJson و @FromJson. المحولات تُسجَّل عبر Moshi.Builder.add() وتُطبَّق عالمياً أو لنوع معين. تدعم Moshi الفئات المغلقة والتسلسل متعدد الأشكال عبر @JsonClass مع محدد تمييز صريح، مما يسمح بالعمل مع تسلسلات هرمية للأنواع في JSON دون اللجوء إلى التحقق اليدوي من الحقول. أثناء إلغاء التسلسل، تتجاهل Moshi المفاتيح غير المعروفة في JSON افتراضياً، مما يضمن التوافق العكسي عند إضافة حقول جديدة على جانب الخادم دون تغيير كود العميل. للتصحيح، يمكن تفعيل الوضع الصارم عبر failOnUnknown، الذي يلقي استثناء عند اكتشاف مفاتيح غير معروفة.

kotlin
// محول مخصص لـ LocalDate
class LocalDateAdapter {

    @ToJson
    fun toJson(date: LocalDate): String {
        return date.format(DateTimeFormatter.ISO_LOCAL_DATE)
    }

    @FromJson
    fun fromJson(dateString: String): LocalDate {
        return LocalDate.parse(dateString)
    }
}

// نموذج مع شروح Moshi
@JsonClass(generateAdapter = true)
data class Event(
    @Json(name = "event_id")
    val id: Int,

    @Json(name = "event_date")
    val date: LocalDate,

    @Transient
    val localCache: String? = null
)

// تسجيل المحول
val moshi = Moshi.Builder()
    .add(LocalDateAdapter())
    .add(KotlinJsonAdapterFactory())
    .build()

Moshi مقابل Gson

مقارنة Moshi و Gson هي سؤال شائع عند اختيار مكتبة JSON لمشروع Android. Moshi تتفوق في تطوير Kotlin الحديث بفضل توليد الكود وأمان القيم الفارغة والسرعة. Gson تبقى مناسبة للمشاريع Java والكود القديم والسيناريوهات حيث تكون التهيئة البسيطة مهمة. يصبح الفرق ملحوظاً مع كميات البيانات الكبيرة والنماذج المعقدة.

الأداء والأمان

تظهر اختبارات الأداء أن Moshi مع توليد الكود أسرع 2–5 مرات من Gson في عمليات التسلسل وإلغاء التسلسل. الميزة الرئيسية لـ Moshi هي المعالجة الصحيحة لأمان القيم الفارغة في Kotlin: إذا كان حقل مفقوداً في JSON وكان النموذج يصرح عنه كـ non-null بدون قيمة افتراضية، تلقي Moshi استثناء في وقت إلغاء التسلسل، مما يمنع الأخطاء المخفية.

الخاصيةGsonMoshi
الآليةانعكاستوليد كود / انعكاس
أمان القيم الفارغةلا يراعيهادعم كامل لـ Kotlin
السرعةمتوسطةعالية
القيم الافتراضيةلا يدعمهايدعمها
Kotlin Multiplatformلانعم
حجم المكتبة~240 كيلوبايت~150 كيلوبايت

الاختيار بين Moshi و Gson يعتمد على سياق المشروع. المشاريع الجديدة على Kotlin تستفيد من Moshi بفضل أمان الأنواع والأداء. Gson تبقى خياراً معقولاً لدعم كود Java والهياكل JSON الديناميكية أو عندما تكون بساطة التهيئة أهم من السرعة. بالنسبة لـ Kotlin Multiplatform، Moshi هي الخيار الوحيد من الاثنين الذي يدعم هذه المنصة.

عند الترحيل من Gson إلى Moshi، التغييرات الرئيسية تتعلق بالشروح والمحولات. @SerializedName من Gson يُستبدل بـ @Json(name = "...")، و JsonSerializer/JsonDeserializer المخصصان بزوج @ToJson/@FromJson. للنماذج ذات القيم الافتراضية والحقول القابلة للفارغية، تتصرف Moshi بشكل أكثر توقعاً: إذا كان حقل non-null بدون قيمة افتراضية مفقوداً في JSON، تلقي Moshi استثناء JsonDataException، مما يمنع أخطاء المؤشر الفارغ المخفية. التكامل مع Retrofit عبر MoshiConverterFactory يُضاف بتبعية واحدة ولا يتطلب تغيير بنية طبقة الشبكة. للغموض عبر ProGuard أو R8، يجب إضافة قواعد للحفاظ على الفئات المشروحة بـ @JsonClass والمحولات المولَّدة، وإلا سينكسر التسلسل في بناء الإصدار. بشكل عام، الترحيل من Gson إلى Moshi مبرر في مشاريع Kotlin الجديدة حيث الأداء وأمان الأنواع مهمان.

kotlin
// مقارنة التسلسل: Gson vs Moshi
data class Sample(
    val name: String,
    val count: Int,
    val tags: List<String> = listOf()
)

// Gson: يعمل عبر الانعكاس
val gson = Gson()
val fromGson = gson.fromJson("""{"name":"test"}""",
    Sample::class.java)
// count = 0 (افتراضي)، لكن أمان القيم الفارغة لا يتم التحقق منه

// Moshi: يتطلب محولاً، أمان القيم الفارغة واضح
@JsonClass(generateAdapter = true)
data class SampleMoshi(
    val name: String,
    val count: Int,
    val tags: List<String> = listOf()
)

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

ما هي Moshi في Android؟

Moshi هي مكتبة JSON من Square لـ Kotlin و Android تستخدم توليد الكود بدلاً من الانعكاس. توفر أداءً عالياً ومعالجة صحيحة لأمان القيم الفارغة في Kotlin وتوافقاً مع Kotlin Multiplatform.

كيف تفوق Moshi على Gson؟

تتفوق Moshi على Gson في السرعة (أسرع 2–5 مرات بفضل توليد الكود)، والأمان (تراعي شروح القيم الفارغة في Kotlin) والحجم (أصغر بحوالي 90 كيلوبايت). Moshi تدعم أيضاً Kotlin Multiplatform والقيم الافتراضية في data class.

كيف يعمل الشرح @JsonClass في Moshi؟

@JsonClass(generateAdapter = true) يوجه Moshi لتوليد محول للفئة المعطاة في وقت الترجمة. المحول المولَّد يقوم بالتسلسل مباشرة دون انعكاس، مما يوفر أقصى أداء.

كيف إنشاء محول مخصص لـ Moshi؟

أنشئ فئة بطريقتين مشروحتين بـ @ToJson (تسلسل) و @FromJson (إلغاء تسلسل). سجل المثيل عبر Moshi.Builder.add(). ستجد Moshi وتطبق المحول تلقائياً عند العمل مع النوع المناسب.

هل تدعم Moshi Kotlin Multiplatform؟

نعم، Moshi تدعم Kotlin Multiplatform بدءاً من الإصدار 1.13.0. وهذا يجعلها حل JSON الوحيد الشائع لمشاريع KMP، مما يسمح باستخدام كود تسلسل مشترك على جميع المنصات المستهدفة.

الخلاصة

  • Moshi « مكتبة JSON حديثة من Square مع توليد كود بدلاً من الانعكاس
  • @JsonClass « شرح لتوليد المحول، مما يوفر سرعة الكود المكتوب يدوياً
  • @Json « تكوين مفاتيح JSON، @Transient « استبعاد الحقول من التسلسل
  • @ToJson و @FromJson « API بسيط للمحولات المخصصة لأي نوع
  • أمان القيم الفارغة « Moshi تراعي شروح Kotlin وتلقي استثناء عند عدم التطابق
  • الأداء « أسرع 2–5 مرات من Gson في عمليات التسلسل وإلغاء التسلسل
  • Kotlin Multiplatform « دعم KMP لكود تسلسل عالمي

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا