Moshi یک کتابخانه مدرن JSON از Square است که به طور ویژه برای Kotlin و Android با در نظر گرفتن محدودیتهای Gson ایجاد شده است. این کتابخانه کاملاً با null-safety کاتلین سازگار است، کد را در مرحله کامپایل تولید میکند و از بازتاب (reflection) استفاده نمیکند که باعث افزایش عملکرد و قابلیت اطمینان میشود. طبق دادههای Square Moshi، 2024، Moshi سریالسازی قابل پیشبینی را تضمین میکند و از آداپتورهای سفارشی برای هر نوع داده پشتیبانی میکند.
نکات اصلی
Moshi یک کتابخانه JSON برای JVM، Android و Kotlin Multiplatform است که توسط Square (سازندگان OkHttp و Retrofit) ایجاد شده است. برخلاف Gson، Moshi به بازتاب متکی نیست — آداپتورها در مرحله کامپایل از طریق حاشیهنویسی @JsonClass(generateAdapter = true) تولید میشوند. این باعث میشود Moshi در کار با ساختارهای خاص Kotlin سریعتر، ایمنتر و قابل پیشبینیتر باشد.
تفاوت اصلی Moshi با predecessors — کنار گذاشتن بازتاب است. بازتاب به Gson اجازه میدهد بدون آمادهسازی با هر کلاسی کار کند، اما بهای آن راهاندازی کند، عدم امکان بهینهسازی توسط کامپایلر و خطر خطا در زمان اجرا است. Moshi نیازمند مشخص کردن صریح کلاسها برای تولید کد است، اما در عوض سرعت کد دست نویس و امنیت کامل نوع در مرحله کامپایل را ارائه میدهد.
// اتصال 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 از طریق Builder، توسعهدهنده یک نمونه Moshi دریافت کرده و برای کلاس مورد نظر آداپتور درخواست میکند. JsonAdapter — شیء مرکزی است که سریالسازی را از طریق toJson() و دیسریالسازی را از طریق fromJson() انجام میدهد. Moshi به طور خودکار از آداپتور تولید شده استفاده میکند اگر کلاس با @JsonClass(generateAdapter = true) حاشیهنویسی شده باشد، در غیر این صورت KotlinJsonAdapterFactory بازتابی را به عنوان گزینه پشتیبان اعمال میکند. این رویکرد سرعت تولید کد را با انعطافپذیری مکانیزم بازتابی برای پروژههای با هر مقیاس و سطح پیچیدگی ترکیب میکند. Moshi هم برای برنامههای کوچک و هم برای پروژههای بزرگ شرکتی با صدها مدل داده عالی است.
// پیکربندی 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 جایگزین @SerializedName گسون میشود و مشابه کار میکند: فیلد kotlinName به کلید JSON «kotlin_name» متصل میشود. برای انواعی که Moshi به طور پیشفرض نمیتواند سریالسازی کند (مثلاً LocalDate)، توسعهدهنده کلاسی با متدهای @ToJson و @FromJson ایجاد میکند. آداپتورها از طریق Moshi.Builder.add() ثبت میشوند و به صورت سراسری یا برای یک نوع خاص اعمال میشوند. Moshi از sealed class و سریالسازی چندریختی از طریق @JsonClass با مشخص کردن صریح تفکیککننده پشتیبانی میکند که کار با سلسله مراتب انواع در JSON را بدون بررسی دستی فیلدها امکانپذیر میکند. در دیسریالسازی، Moshi به طور پیشفرض کلیدهای ناشناخته در JSON را نادیده میگیرد که سازگاری معکوس را هنگام اضافه شدن فیلدهای جدید در سمت سرور بدون تغییر کد مشتری تضمین میکند. برای اشکالزدایی میتوان حالت سختگیرانه را از طریق failOnUnknown فعال کرد که در صورت تشخیص کلیدهای ناشناخته استثنا ایجاد میکند.
// آداپتور سفارشی برای 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 یک سوال رایج هنگام انتخاب کتابخانه JSON برای پروژه Android است. Moshi در توسعه مدرن Kotlin به دلیل تولید کد، null-safety و سرعت برنده است. Gson برای پروژههای Java، کدهای قدیمی و سناریوهایی که پیکربندی حداقلی مهم است، همچنان مرتبط باقی میماند. تفاوت در حجمهای زیاد داده و مدلهای پیچیده قابل توجه میشود.
تستهای عملکرد نشان میدهند که Moshi با تولید کد 2-5 برابر سریعتر از Gson در عملیات سریالسازی و دیسریالسازی کار میکند. مزیت کلیدی Moshi — پردازش صحیح null-safety کاتلین: اگر فیلدی در JSON وجود نداشته باشد و در مدل به صورت non-null بدون مقدار پیشفرض اعلام شده باشد، Moshi در مرحله دیسریالسازی استثنا ایجاد میکند و از خطاهای پنهان جلوگیری میکند.
| ویژگی | Gson | Moshi |
|---|---|---|
| مکانیزم | بازتاب | تولید کد / بازتاب |
| Null safety | در نظر نمیگیرد | پشتیبانی کامل از Kotlin |
| سرعت | متوسط | بالا |
| مقادیر پیشفرض | پشتیبانی نمیکند | پشتیبانی میکند |
| Kotlin Multiplatform | خیر | بله |
| اندازه کتابخانه | ~240 کیلوبایت | ~150 کیلوبایت |
انتخاب بین Moshi و Gson به زمینه پروژه بستگی دارد. پروژههای جدید روی Kotlin از Moshi به دلیل امنیت نوع و عملکرد بهره میبرند. Gson برای پشتیبانی از کد Java، ساختارهای JSON پویا یا زمانی که سادگی اتصال مهمتر از سرعت است، انتخاب منطقی باقی میماند. برای Kotlin Multiplatform، Moshi تنها گزینه از این دو است که از این پلتفرم پشتیبانی میکند.
در مهاجرت از Gson به Moshi، تغییرات اصلی مربوط به حاشیهنویسیها و آداپتورها است. @SerializedName گسون با @Json(name = "...") و JsonSerializer/JsonDeserializer سفارشی با جفت @ToJson/@FromJson جایگزین میشوند. برای مدلهای با مقادیر پیشفرض و فیلدهای nullable، Moshi رفتار قابل پیشبینیتری دارد: اگر فیلد non-null بدون مقدار پیشفرض در JSON وجود نداشته باشد، Moshi JsonDataException ایجاد میکند و از NPE پنهان جلوگیری میکند. ادغام با Retrofit از طریق MoshiConverterFactory با یک وابستگی اضافه میشود و نیازی به تغییر معماری لایه شبکه ندارد. برای مبهمسازی از طریق ProGuard یا R8 باید قوانین حفظ کلاسهای حاشیهنویسی شده با @JsonClass و آداپتورهای تولید شده اضافه شود، در غیر این صورت سریالسازی در نسخه release خراب میشود. به طور کلی، مهاجرت از Gson به Moshi در پروژههای جدید Kotlin که عملکرد و امنیت نوع مهم هستند، توجیهپذیر است.
// مقایسه سریالسازی: Gson در مقابل 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 (default), اما null-safety بررسی نمیشود
// Moshi: نیاز به آداپتور دارد، null-safety صریح است
@JsonClass(generateAdapter = true)
data class SampleMoshi(
val name: String,
val count: Int,
val tags: List<String> = listOf()
)
سوالات متداول
Moshi یک کتابخانه JSON از Square برای Kotlin و Android است که از تولید کد به جای بازتاب استفاده میکند. این کتابخانه عملکرد بالا، پردازش صحیح null-safety کاتلین و سازگاری با Kotlin Multiplatform را تضمین میکند.
Moshi از Gson در سرعت (2-5 برابر سریعتر به دلیل تولید کد)، امنیت (حاشیهنویسیهای null کاتلین را در نظر میگیرد) و اندازه (~90 کیلوبایت کوچکتر) پیشی میگیرد. Moshi همچنین از Kotlin Multiplatform و مقادیر پیشفرض در data class پشتیبانی میکند.
@JsonClass(generateAdapter = true) به Moshi دستور میدهد یک آداپتور برای این کلاس در مرحله کامپایل تولید کند. آداپتور تولید شده سریالسازی را مستقیماً و بدون بازتاب انجام میدهد که حداکثر عملکرد را ارائه میدهد.
کلاسی با متدهای حاشیهنویسی شده با @ToJson (سریالسازی) و @FromJson (دیسریالسازی) ایجاد کنید. نمونه را از طریق Moshi.Builder.add() ثبت کنید. Moshi به طور خودکار آداپتور را هنگام کار با نوع مربوطه پیدا کرده و اعمال میکند.
بله، Moshi از نسخه 1.13.0 از Kotlin Multiplatform پشتیبانی میکند. این آن را به تنها راهحل محبوب JSON برای پروژههای KMP تبدیل میکند و امکان استفاده از کد سریالسازی مشترک در تمام پلتفرمهای هدف را فراهم میکند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید