Jackson في Kotlin: كيفية إصلاح الخطأ "No Creators, like default construct, exist"

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: لماذا يكسر كود Kotlin الخاص بك Jackson فجأة؟

\n\n

هل سبق لك أن كتبت data class جميلاً ومختصراً في Kotlin، وأضفت إليه بضع حقول، وقمت بتشغيل إلغاء تسلسل JSON عبر Jackson - وفجأة تلقيت خطأ طويلاً ومخيفاً في وجهك؟ مألوف؟ إذا كان الأمر كذلك، فأنت لست وحدك. الخطأ "No Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator" هو أحد أكثر الاستعلامات شيوعاً على Stack Overflow (آلاف المشاهدات!) بين المطورين الذين ينتقلون من Java إلى Kotlin.

\n\n

في هذه المقالة، لن نقوم فقط بترجمة نص الخطأ. سنقوم بتحليله "تحت المجهر": لماذا يحدث، وكيفية إعادة إنتاجه، والأهم من ذلك - كيفية التخلص منه نهائياً. ستحصل ليس فقط على النظرية، بل أيضاً على حلول جاهزة ومختبرة مع كود Kotlin. هيا بنا!

\n\n

ماذا يعني هذا الخطأ بالضبط؟ بكلمات بسيطة

\n\n

لنتخيل أن Jackson هو "باني" يحتاج إلى تجميع كائن من صنفك من مكعبات JSON. لبناء المنزل، يحتاج الباني إلى مخطط واضح (مُنشئ) أو على الأقل مجموعة من الجدران الجاهزة (الحقول).

\n\n

الخطأ "No Creators, like default construct, exist" يقول حرفياً: "عزيزي Jackson، لا يمكنني إنشاء كائن من هذا الصنف لأنه ليس لديه مُنشئ افتراضي (بدون معاملات)، ولا أعرف كيفية تجميعه من حقول JSON المرسلة!"

\n\n

في Java، الأمر بسيط: إذا لم تكتب أي مُنشئ، يقوم المترجم بلطف بإنشاء مُنشئ افتراضي فارغ. يراه Jackson ويستخدمه بسعادة. في Kotlin، إذا قمت بتعريف data class مع معاملات في المُنشئ، فإن المُنشئ بدون معاملات لا يتم إنشاؤه تلقائياً. و Jackson، بشكل افتراضي، يبحث عنه تحديداً.

\n\n

إعادة إنتاج المشكلة: كود سيتسبب في الخطأ

\n\n

لنكتب مثالاً بسيطاً سيؤدي بالتأكيد إلى هذا الخطأ. لنفترض أن لدينا صنف مستخدم:

\n\n
import com.fasterxml.jackson.databind.ObjectMapper\nimport com.fasterxml.jackson.module.kotlin.readValue\nimport com.fasterxml.jackson.module.kotlin.jacksonObjectMapper\n\n// انتبه: هذا الكود سيسبب خطأ!\ndata class User(val name: String, val age: Int)\n\nfun main() {\n    val mapper: ObjectMapper = jacksonObjectMapper()\n    val json = """{"name": "أليكسي", "age": 30}"""\n\n    // نحاول إلغاء تسلسل JSON إلى كائن User\n    val user: User = mapper.readValue(json)\n    println(user)\n}\n
\n\n

قم بتشغيل هذا الكود، وستحصل على الاستثناء المطلوب:

\n\n
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: \nCannot construct instance of `User` (no Creators, like default construct, exist): \ncannot deserialize from Object value (no delegate- or property-based Creator)\n at [Source: (String)"{"name": "أليكسي", "age": 30}"; line: 1, column: 2]\n
\n\n

ها هي، "نجمة" Stack Overflow الخاصة بنا! لماذا حدث هذا؟ لأن data class User(val name: String, val age: Int) ليس لديه مُنشئ بدون معاملات. جميع حقوله معلنة في المُنشئ الأساسي، وهي إلزامية (val). لا يمكن لـ Jackson إنشاء User دون معرفة القيم التي يجب تمريرها إلى name و age، وبدون وجود تلميحات على شكل دوال تعيين (setters).

\n\n

3 طرق لإصلاح الخطأ (من البسيط إلى الصحيح)

\n\n

الخبر السار: هناك عدة حلول. يعتمد الاختيار على مدى رغبتك في التحكم في العملية وأي كود تعتبره "نظيفاً".

\n\n

الطريقة 1: الأسهل (ولكن ليس لعشاق Kotlin النقيين)

\n\n

إضافة مُنشئ بدون معاملات يدوياً. في Kotlin، يتم ذلك عن طريق تحديد قيم افتراضية لجميع الحقول.

\n\n
import com.fasterxml.jackson.databind.ObjectMapper\nimport com.fasterxml.jackson.module.kotlin.readValue\nimport com.fasterxml.jackson.module.kotlin.jacksonObjectMapper\n\n// نعم                

المدونات

توصيات الكتب