Moshi เป็นไลบรารี JSON สมัยใหม่จาก Square ที่สร้างขึ้นโดยเฉพาะสำหรับ Kotlin และ Android โดยคำนึงถึงข้อจำกัดของ Gson มันเข้ากันได้อย่างสมบูรณ์กับความปลอดภัย null ของ Kotlin สร้างโค้ดในเวลาคอมไพล์และไม่ใช้รีเฟลกชัน ซึ่งช่วยเพิ่มประสิทธิภาพและความน่าเชื่อถือ ตามข้อมูลจาก Square Moshi, 2024 Moshi ให้การซีเรียลไลเซชันที่คาดการณ์ได้ และรองรับอะแดปเตอร์แบบกำหนดเองสำหรับประเภทข้อมูลใด ๆ
ประเด็นสำคัญ
Moshi เป็นไลบรารี JSON สำหรับ JVM, Android และ Kotlin Multiplatform ที่สร้างโดย Square (ผู้สร้าง OkHttp และ Retrofit) ต่างจาก Gson ตรงที่ Moshi ไม่พึ่งพารีเฟลกชัน — อะแดปเตอร์จะถูกสร้างขึ้นในเวลาคอมไพล์ผ่านคำอธิบายประกอบ @JsonClass(generateAdapter = true) ทำให้ Moshi เร็วขึ้น ปลอดภัยขึ้น และคาดการณ์ได้มากขึ้นเมื่อทำงานกับโครงสร้างเฉพาะของ Kotlin
ความแตกต่างหลักระหว่าง Moshi และรุ่นก่อนคือการปฏิเสธรีเฟลกชัน รีเฟลกชัน ช่วยให้ 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 คุณต้องเพิ่ม dependencies ใน 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 ของ Gson และทำงานคล้ายกัน: ฟิลด์ kotlinName เชื่อมโยงกับคีย์ JSON "kotlin_name" สำหรับประเภทที่ Moshi ไม่สามารถซีเรียลไลซ์โดยค่าเริ่มต้น (เช่น LocalDate) นักพัฒนาจะสร้างคลาสที่มีเมธอด @ToJson และ @FromJson อะแดปเตอร์ ลงทะเบียนผ่าน Moshi.Builder.add() และนำไปใช้ทั่วโลกหรือกับประเภทเฉพาะ Moshi รองรับคลาส sealed และการซีเรียลไลเซชันแบบพหุรูปผ่าน @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 และความเร็ว Gson ยังคงเกี่ยวข้องสำหรับโปรเจกต์ Java โค้ดเก่า และสถานการณ์ที่การกำหนดค่าขั้นต่ำมีความสำคัญ ความแตกต่างจะสังเกตเห็นได้ชัดเจนกับข้อมูลปริมาณมากและโมเดลที่ซับซ้อน
การทดสอบประสิทธิภาพแสดงให้เห็นว่า Moshi ที่มีการสร้างโค้ดนั้น เร็วกว่า 2–5 เท่า เมื่อเทียบกับ Gson ในการดำเนินการซีเรียลไลเซชันและดีซีเรียลไลเซชัน ข้อได้เปรียบหลักของ Moshi คือการจัดการความปลอดภัย null ของ Kotlin อย่างถูกต้อง: หากฟิลด์หายไปใน JSON และโมเดลประกาศเป็น non-null โดยไม่มีค่าเริ่มต้น Moshi จะโยนข้อยกเว้นในเวลาดีซีเรียลไลเซชัน ป้องกันข้อผิดพลาดที่ซ่อนอยู่
| คุณลักษณะ | Gson | Moshi |
|---|---|---|
| กลไก | รีเฟลกชัน | การสร้างโค้ด / รีเฟลกชัน |
| ความปลอดภัย null | ไม่พิจารณา | รองรับ Kotlin เต็มรูปแบบ |
| ความเร็ว | ปานกลาง | สูง |
| ค่าเริ่มต้น | ไม่รองรับ | รองรับ |
| Kotlin Multiplatform | ไม่ | ใช่ |
| ขนาดไลบรารี | ~240 Kb | ~150 Kb |
การเลือกระหว่าง Moshi และ Gson ขึ้นอยู่กับบริบทของโปรเจกต์ โปรเจกต์ใหม่บน Kotlin ได้รับประโยชน์จาก Moshi ด้วยความปลอดภัยของประเภทและประสิทธิภาพ Gson ยังคงเป็นตัวเลือกที่สมเหตุสมผลสำหรับการรองรับโค้ด Java โครงสร้าง JSON แบบไดนามิก หรือเมื่อความเรียบง่ายในการกำหนดค่าสำคัญกว่าความเร็ว สำหรับ Kotlin Multiplatform Moshi เป็นตัวเลือกเดียวจากสองตัวเลือกที่รองรับแพลตฟอร์มนี้
เมื่อย้ายจาก Gson ไป Moshi การเปลี่ยนแปลงหลักเกี่ยวข้องกับคำอธิบายประกอบและอะแดปเตอร์ @SerializedName ของ Gson ถูกแทนที่ด้วย @Json(name = "...") และ JsonSerializer/JsonDeserializer แบบกำหนดเองด้วยคู่ @ToJson/@FromJson สำหรับโมเดลที่มีค่าเริ่มต้นและฟิลด์ nullable Moshi มีพฤติกรรมที่คาดการณ์ได้มากขึ้น: หากฟิลด์ non-null โดยไม่มีค่าเริ่มต้นหายไปใน JSON Moshi จะโยน JsonDataException ป้องกัน NPE ที่ซ่อนอยู่ การรวมเข้ากับ Retrofit ผ่าน MoshiConverterFactory ถูกเพิ่มด้วย dependency เดียวและไม่ต้องการเปลี่ยนสถาปัตยกรรมเลเยอร์เครือข่าย สำหรับการทำให้สับสนผ่าน ProGuard หรือ R8 จำเป็นต้องเพิ่มกฎเพื่อรักษาคลาสที่ใส่คำอธิบายประกอบ @JsonClass และอะแดปเตอร์ที่สร้างขึ้น มิฉะนั้นการซีเรียลไลเซชันจะเสียในบิลด์ที่เผยแพร่ โดยรวมแล้ว การย้ายจาก Gson ไป Moshi นั้นสมเหตุสมผลในโปรเจกต์ 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 (ค่าเริ่มต้น) แต่ความปลอดภัย null ไม่ถูกตรวจสอบ
// Moshi: ต้องการอะแดปเตอร์ ความปลอดภัย null ชัดเจน
@JsonClass(generateAdapter = true)
data class SampleMoshi(
val name: String,
val count: Int,
val tags: List<String> = listOf()
)
คำถามที่พบบ่อย
Moshi เป็นไลบรารี JSON จาก Square สำหรับ Kotlin และ Android ที่ใช้การสร้างโค้ดแทนรีเฟลกชัน ให้ประสิทธิภาพสูง การจัดการความปลอดภัย null ของ Kotlin อย่างถูกต้อง และความเข้ากันได้กับ Kotlin Multiplatform
Moshi เหนือกว่า Gson ในด้านความเร็ว (เร็วกว่า 2–5 เท่าด้วยการสร้างโค้ด) ความปลอดภัย (เคารพคำอธิบายประกอบ null ของ Kotlin) และขนาด (เล็กกว่าประมาณ 90 Kb) Moshi ยังรองรับ Kotlin Multiplatform และค่าเริ่มต้นใน data class
@JsonClass(generateAdapter = true) แนะนำให้ Moshi สร้างอะแดปเตอร์สำหรับคลาสที่กำหนดในเวลาคอมไพล์ อะแดปเตอร์ที่สร้างขึ้นจะทำการซีเรียลไลเซชันโดยตรงโดยไม่ต้องรีเฟลกชัน ให้ประสิทธิภาพสูงสุด
สร้างคลาสที่มีเมธอดที่ใส่คำอธิบายประกอบด้วย @ToJson (การซีเรียลไลเซชัน) และ @FromJson (การดีซีเรียลไลเซชัน) ลงทะเบียนอินสแตนซ์ผ่าน Moshi.Builder.add() Moshi จะค้นหาและใช้อะแดปเตอร์โดยอัตโนมัติเมื่อทำงานกับประเภทที่เกี่ยวข้อง
ใช่ Moshi รองรับ Kotlin Multiplatform ตั้งแต่เวอร์ชัน 1.13.0 ทำให้เป็นโซลูชัน JSON ยอดนิยมเพียงตัวเดียวสำหรับโปรเจกต์ KMP ซึ่งอนุญาตให้ใช้โค้ดการซีเรียลไลเซชันร่วมกันบนแพลตฟอร์มเป้าหมายทั้งหมด
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม