آموزشدانستنی‌ها

JSON با Jackson (بخش چهارم-ب) Data Binding

شرح بیشتر توانمندی‌های روش Data Binding در این مقاله ادامه پیدا کرده و تکمیل می‌شود.

نوشته‌ی حاضر، در ادامه مقاله قبل، امکانات روش Data Binding را بیشتر شرح داده‌ و به پاسخ سوال‌های زیر خواهد پرداخت: تولید و بازیابی نمونه از کلاس‌های یک ساختار ۱hierarchy به شکل ۲polymorphic امکان پذیر است؟ آیا می‌توان هنگام تولید و بازیابی از یک سری فیلد‌ها صرف نظر کرده و آنها را نادیده گرفت؟ آیا می‌توان انعطافی را که به کمک annotation‌ها در عملکرد Jackson ایجاد می‌کنیم، بر روی کلاس‌های کتابخانه‌های third-party نیز اعمال کنیم؟ در انتها به این سوال خواهیم پرداخت: «در چه مواردی Data Binding Method بهترین گزینه است؟»

Josn Polymorhpism

Jackson روشی برای نگهداری نوع زیرکلاس‌ها (Sub-Class)، هنگام ساخت JSON از اشیای جاوا در اختیار قرار می‌دهد. با استفاده از این تسهیلات هنگام ساخت دوباره‌ی اشیاء جاوا از JSON می‌توان نوع دقیق زیرکلاس را بازیابی کرد. اطلاعات نوع زیرکلاس می‌تواند در داخل JSON به عنوان یک property ذخیره شود. در مثال زیر، مثال Library باز طراحی شده تا کار با کلاس‌های polymorphic قابل بررسی شود. به این منظور کلاس Library را تغییر دادیم تا لیستی از Document‌ها را نگهداری کند. Document یک کلاس abstract است. زیرکلاس‌های آن عبارتند از Journal و Book. تعریف مربوط به هر کدام از کلاس‌ها در ادامه آورده می‌شود:

Document توسط دو کلاس زیر به ارث برده می شود. کلاس Journal:

و کلاس Book:

کلاس Library به شکل زیر تعریف شده است، به فیلد docList در این کلاس دقت کنید.

با کمک تابع زیر شیء‌ای از کلاس Library را ساخته و برای تولید JSON از آن استفاده خواهیم کرد:

با اجرای دو سطر زیر، JSON تولید شده و در فایل ذخیره می‌شود:

JSON تولید شده به شکل زیر است:

همان طور که می‌بینید هیچ اطلاعاتی در این مورد که شیء با isbn=1 از نوع Book است در این رشته وجود ندارد. هنگامی که می‌خواهیم این داده را تبدیل به POJO کنیم، در ابتدا Jackson تلاش می‌کند اشیاء داخل docList را تبدیل به نمونه‌هایی از Document کند. اما چون Document یک کلاس abstract است، یک exception پرتاب می‌کند.

این موضوع را با اجرای کد زیر بررسی می‌کنیم:

خروجی زیر تولید می‌شود:

پیشنهاد Jackson این است که اطلاعاتی در مورد نوع اشیا (additional type information) در کنار کلاس Document عرضه شود.

برای ذخیره‌ی «اطلاعات نوع» از دو annotation با نام‌های JsonTypeInfo و JsonSubTypes استفاده می‌کنیم. اطلاعات مربوط به زیرکلاس‌های Document به شکل زیر در کد نوشته می‌شود. دقت کنید که این اطلاعات در کنار کد ابَر‌کلاس (یعنی Document) ذکر شده است.

پس از اعمال تغییرات فوق، با تبدیل شیء‌ای از کلاس Library به JSON، خروجی زیر تولید می‌شود:

همانطور که می‌بینیم، به هر کدام از نمونه‌های Document یک فیلد با نام class@ اضافه شده و نام کامل کلاس که شامل نام package هم هست به عنوان value نوشته شده است. در صورتی که داده JSON به این صورت نوشته شود قادر به بازیابی کامل library خواهیم بود.

بررسی دقیق‌تر annotation‌های مربوط به «اطلاعات نوع»

JsonTypeInfo@ که در کد فوق مورد استفاده قرار گرفت دارای سه پارامتر است.

  • پارامتر use دارای مقداری از نوع JsonTypeInfo.Id Enum است. این پارامتر بیان می‌کند کلاس مربوط به این شیء چگونه معرفی شود. به عنوان مثال اگر این پارامتر از نوع Id.CLASS باشد نام کامل کلاس (همراه با نام پکیج) و اگر از نوع Id.MINIMAL_CLASS باشد، نام کلاس بدون نام پکیج نوشته می‌شود.
  • پارامتر دوم بیان می‌کند اطلاعات مربوط به نوع کلاس به چه شکل در JSON نوشته شود، این پارامتر به صورت یک مقدار از Enum JsonTypeInfo.As نوشته می‌شود. به عنوان مثال اگر include برابر با As.PROPERTY باشد نوع کلاس به عنوان یک فیلد نوشته می شود:

و اگر از نوع As.WRAPPER_OBJECT نوشته شود، شکل ذخیره‌ی داده به شکل زیر تغییر می کند:

  • پارامتر سوم یا property، نام فیلدی که نوع کلاس را در JSON ذخیره خواهد کرد، نشان می‌دهد.

JsonSubTypes@ لیستی از زیرکلاس‌ها را به شکل Type@ دریافت می‌کند.

مدیریت فیلد‌هایی که باید Serialize و Deserialize شوند

در مثال‌های قبل برای تبدیل یک شیء به JSON آن‌را در اختیار ObjectMaper قرار می‌دادیم، این کلاس فیلد‌های شیء را به صورت «اتوماتیک» «شناسایی» کرده و بعد نام و مقدار آن‌ها را در محل مناسب می‌نوشت. در این قسمت می‌خواهیم روی «شناسایی اتوماتیک» کنترل بیشتری داشته باشیم. به این منظور از annotation ای به نام JsonAutoDetect@ استفاده خواهیم کرد.

از کلاس زیر به منظور بررسی عملکرد این annotation استفاده خواهیم کرد. کلاس، دارای فیلدی از هر چهار نوع Access Control است. getter و setter هر فیلد نیز هم نوع با خود فیلد تعریف شده‌اند. (هر چند در برنامه نویسی معمول چنین کلاسی کاربردی نخواهد داشت ولی به درک بحث کمک می‌کند)

اگر شیء از کلاس فوق ایجاد کرده و طبق روال معمول آن را به JSON تبدیل کنیم خروجی زیر تولید می‌شود:

JsonAutoDetect@ دارای ورودی‌هایی مثل getterVisibility یا fieldVisibility است که مقداری از نوع JsonAutoDetect.Visibility دریافت می‌کند. اگر annotation زیر را به FieldyClass اضافه کنیم:

و با این کلاس یک شیء ساخته و تبدیل به JSON کنیم، خروجی زیر تولید می‌شود.

مثال بعدی حالتی است که تمام getter‌ها را قابل مشاهده کنیم تا مقدار فیلد معادل در خروجی نوشته شود:

خروجی زیر را تولید می کند:

در آخرین مثال این بخش دسترسی توسط getter‌ها را حذف کرده و فیلد‌های غیر private را نمایان خواهیم کرد.

حاصل کار به شکل زیر قابل مشاهده است:

علاوه بر تنظیم فوق annotation‌هایی مانند JsonIgnore@ و JsonIgnoreProperties@ در دسترس است. مثالی از کاربرد اولی در بخش مربوط به JsonCreator، نشان داده شد. مثال زیر عملکرد آنها را به صورت ساده‌ای نشان می دهد. این کلاس دارای چهار فیلد است، فیلد اول و دوم را به کمک JsonIgnoreProperties@ و فیلد سوم را به کمک JSonIgnore@، از لیست مواردی که در رشته‌ی JSON نوشته می‌شوند، حذف می‌کنیم.

خروجی تولید شیء ای از جنس این کلاس و نوشتن آن به شکل JSON توسط ObjectMapper به شکل زیر است:

JsonIgnore@ برای متد‌، Constructor و فیلد‌ها قابل استفاده است. JsonIgnoreProperties@ علاوه بر موارد فوق بر روی Type‌ها (کلاس‌ها و Interface‌ها) هم استفاده می‌شود.

اعمال annotation‌های Jackson روی یک کلاس، بدون تغییر کد آن

annotation‌ها یک ابزار بسیار عالی برای مدیریت روال تولید و خواندن JSON یا (serialization and deserialization) هستند. اما اگر کد‌های کلاسی که می‌خواهیم آن را تبدیل به JSON کنیم تحت اختیار ما نباشد، چه باید کرد؟

کلاس‌های موجود در بسته‎های نرم‌افزاری از این سنخ هستند.

همچنین در بسیاری از موارد نوشته شدن annotation‌های Jackson روی POJO‌های پروژه مطلوب نیست. در مواردی گفته می‌شود باعث پیچیدگی کد و ناخوانا شدن آن است. همچنین موارد بسیاری وجود دارد که POJO به اشکال مختلفی مورد استفاده قرار می‌گیرد و «تبدیل شدن به JSON» تنها یکی از این موارد است. به عنوان مثال یک POJO می‌تواند هم‌زمان Entity پایگاه‌داده هم باشد.

در این موارد از روش Mix-In Annotation استفاده خواهیم‌کرد. روش کار به این صورت است که annotation‌هایی از Jackson که باید روی بخش‌های مختلف کلاس A اعمال می‌شدند، روی abstract class B اعمال می‌کنیم و سپس به کلاس ObjectMapper اعلام می‌شود، annotation‌ها را از کلاس B دریافت کرده و روی A اعمال کند. این کار به کمک متد addMixInAnnotaions از ObjectMapper انجام می‌شود.

مثال ساده زیر کاربرد و روش پیاده‌سازی این مفهوم را نشان می‌دهد.

در این مثال یک دانش آموز به صورت نمونه‌ای از کلاس Student بیان می‌شود. سه فیلد این کلاس عبارتند از lastName ،firstName و privateHealthCondition. مطلوب است JSON با فیلد‌های foreName و surname تولید شود. همچنین وضعیت سلامتی دانش‌آموز هم محرمانه بوده و نباید تبدیل به JSON گردد. برای رسیدن به این هدف دو کلاس زیر را ایجاد می‌کنیم.

annotation‌های مربوط به تنظیمات تولید JSON در کلاس زیر متمرکز شده اند:

با اجرای کد زیر نمونه‌‌ای از کلاس Student ساخته می‌شود و پس از معرفی کلاس MixIn به ObjectMapper داده‌ی JSON ای در یک فایل نوشته می‌شود. تنظیماتی که در کلاس StudentMixIn اعمال کرده‌ایم در تبدیل Student در نظر گرفته خواهند شد.

خروجی اجرای کد فوق، داده‌ی متنی زیر است:

نتیجه‌ی فوق نشان می‌دهد، فیلد‌های مربوط به نام، تغییر کرده و همچنین فیلد مربوط به سلامتی دانش آموز هنگام تبدیل به JSON نادیده گرفته شده‌است.

ویژگی‌های Data Binding

Data Binding با هدف تسهیل تبدیل POJO به JSON و بالعکس توسعه یافته است. در صورتی که در میان کلاس‌های پروژه، کلاسی ای معادل داده ی JSON موجود باشد، این روش در میان دو گزینه ی دیگر یعنی Streaming و Tree Model برتری قابل ملاحظه‌ای دارد و بدون شک بهترین انتخاب به شمار می‌رود.


۱- hierarchy: به معنی سلسله‌مراتبی، ساختاری که در آن، هر کدام از اعضا با شاخص‌هایی رتبه‌بندی و مرتب شده‌اند. در مورد برنامه نویسی شیء‌گرایی، می‌توان کلاس‌هایی که از هم ارث‌بری دارند را در یک ساختار مرتب کرد به شکلی که کلاس‌های توسعه داده شده (یا ابرکلاس‌ها) در جایگاه بالاتر، قرار داده شوند. از این ساختار به hierarchy یاد می‌شود.

۲- polymorphism: به چند‌ریختی ترجمه می‌شود. در یک ساختار hierarchy از کلاس‌ها، یک اشاره‌گر به ابرکلاس می‌تواند به نمونه‌هایی از زیر‌کلاس‌های متفاوت اشاره کند. به این اصل و ویژگی‌های رفتاری که از آن نشات می‌گیرد، polymorphism گفته می‌شود.

بخش پنجم

.

.

.

با ما همراه باشید

آدرس کانال تلگرام: JavaCupIR@

آدرس اکانت توییتر: JavaCupIR@

آدرس صفحه اینستاگرام: javacup.ir

آدرس گروه لینکدین: Iranian Java Developers

نوشته های مشابه

‫۴ دیدگاه ها

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا