۲۰ مهر ۱۴۰۴
گولنگ مانند باقی زبان های برنامه نویسی از JSON پشتیبانی میکند. در اینجا با نحوه استفاده از جیسان در برنامه های گولنگی آشنا میشیم.
جیسان (JSON) یا JavaScript Object Notation، یک فرمت ساده و قابلخواندن برای تبادل دادهها است. از لحاظِ ساختارِ ظاهری, داده ها در JSON شبیه اشیا و یا آرایه ها در جاوا اسکریپت هستند. تقریبا تمام زبانهای برنامه نویسی از JSON پشتیبانی میکنند و گولنگ نیز یکی از این زبان ها است.
در گولنگ یکی از روش های کار با جیسان استفاده از پکیج استاندارد JSON است.
برای اینکد کردن داده (encode) به جیسان از تابع Marshal استفاده میکنیم. این تابع دارای یک پارامتر ورودی و دو پارامتر خروجی به شکل زیر است:
در صورتی که خطایی رخ ندهد اولین پارامتر خروجی دارای داده با فرمت جیسان و مقدار error برابر با nil خواهد بود.
یکسری نکات در هنگام استفاده از این تابع وجود دارد:
چنل ها (channel) , اعداد مختلط (complex) و فانکشن ها را نمیتوان به جیسان تبدیل کرد.
ساختارهای حلقوی (cyclic) پشتیبانی نمیشوند. استفاده از ساختارهای حلقوی باعث ایجاد یک حلقه بینهایت در این تابع میشوند.
اشاره گرها (pointers) به مقادیری که به آن اشاره میکنند اینکد خواهند شد. یا اگر اشاره گر nil باشد به صورت null اینکد میشود.
Marshal و Unmarshal استفاده میشوند که قابل دسترسی باشند! یعنی scope آنها طوری باشد که بتوان به آن فیلدها دسترسی داشت.در صورتی که از struct استفاده میکنیم, میتوانیم با استفاده از تگها مشخص کنیم که هر فیلد دقیقا با چه نامی در JSON نهایی اینکد (encode یا marshal) شود:
در مثال بالا فیلد Name با نام first_name به JSON تبدیل میشود. همچنین فیلد Body با نام payload اینکد میشود. بدین صورت با استفاده از تگ json میتوانیم نام فیلدها را دقیقا مشخص کنیم.
برای دیکد کردن JSON از تابع Unmarshal استفاده میکنیم. پارامترهای این تابع به شکل زیر است:
این تابع دارای دو پارامتر ورودی و یک مقدار خروجی است. اولین پارامتر ورودی اطلاعات با فرمت JSON هستند, دومین پارامتر باید یک اشاره گر به یک متغیر باشد. این تابع سعی میکند تا داده های پارامتر اول را که با فرمت JSON هستند درون متغیری که به عنوان پارامتر دوم مشخص کرده ایم ذخیره کند و در صورتی که کار موفقیت آمیز باشد مقدار error برابر با nil خواهد بود.
کاربرد این تابع به شکل زیر است:
در مثال بالا اگر b حاوی JSON معتبری باشد که در m جا شود، پس از فراخوانی تابع Unmarshal مقدار err برابر با nil خواهد بود و دادههای b در ساختار m ذخیره خواهند شد و در نهایت در خروجی استاندارد (STDOUT) چاپ میشوند.
Unmarshal چگونه فیلد متناظر با داده های دیکد (decode یا unmarshal) شده را تشخیص میدهد؟برای یک کلید JSON با نام «Foo»، تابع Unmarshal به ترتیب زیر در فیلدهای متغیری که به عنوان پارامتر دوم پاس داده ایم جستوجو میکند و در صورتی که تطبیق انجام شود اطلاعات را در آن فیلد ذخیره میکند:
ابتدا به دنبال فیلدی میگردد که قابل دسترسی باشد (scope) و دارای تگ (tag) با نام «Foo» باشد.
در صورت پیدا نکردن مورد بالا، به دنبال فیلدی میگردد که قابل دسترسی (scope) و دارای نام دقیق «Foo» باشد.
اگر همچنان موردی یافت نشود، به دنبال فیلدی میگردد که قابل دسترسی (scope) و نام آن با «Foo» به صورت غیرحساس به حروف بزرگ و کوچک (case-insensitive) مطابقت داشته باشد؛ مانند «FOO» یا «FoO».
در حالتی که ساختار JSON با دیتاتایپ متغیری که میخواهیم اطلاعات درون آن قرار گیرند منطبق نباشد, تابع Unmarshal تنها فیلدهایی را مقداردهی میکند که بتواند آنها را شناسایی کند. در مثال زیر، فقط فیلد Name در متغیر m مقداردهی خواهد شد و فیلد Food نادیده گرفته میشود.
این ویژگی زمانی بسیار مفید است که بخواهید از یک دادهی JSON بزرگ، تنها از چند فیلد خاص درون برنامه استفاده کنید. علاوه بر این، این رفتار نشان میدهد که در متغیر مقصد, فیلدهایی که قابل دسترسی نیستند (scope)، تحت تأثیر عملیات Unmarshal قرار نمیگیرند و مقدار آنها بدون تغییر باقی میماند.
در صورتی که از struct استفاده می کنیم, با استفاده از تگ گذاری فیلد ها میتوان مشخص کرد که اطلاعات در هنگام Marshal و Unmarshal درون چه فیلدی انکد (encode) و دیکد (decode) شوند.
بدون آنکه ساختار دادهی JSON را از پیش بدانیم، میتوانیم آن را با استفاده از تابع Unmarshal در یک متغیر از نوع interface{} دیکد (decode) کنیم.
در مثال بالا اطلاعات مانند یک مپ (map) که کلیدهای آن از نوع string و مقادیر از نوع interface{} هستند درون متغیر f دیکد می شوند, چیزی همانند مثال زیر:
در این حالت برای دسترسی به اطلاعات میتونیم با استفاده از interface assertion نوع f را از interface{} به map[string]interface{} تبدیل کنیم و سپس به اطلاعات دسترسی داشته باشیم:
پکیج json در زبان Go از نوعهای map[string]interface{} و []interface{} برای نگهداری اشیاء (object) و آرایهها (array) استفاده میکند. یعنی قادر است تا هر دادهی JSON معتبری را بدون مشکل درون یک متغیر از نوع interface{} دیکد (unmarshal) کند. در این حالت نوعهای پیشفرضی که Go برای دادههای JSON در نظر میگیرد به صورت لیست زیر هستند:
bool برای مقدارهای بولی (true / false)float64 برای عددهاstring برای رشتههاnil برای مقدار nullبا استفاده از لیست بالا ما در مثال قبل یک حلقه for نوشتیم که روی اطلاعاتی که دیکد شده است حرکت میکند و هرکدام از مقادیر را در خروجی استاندارد (stdout) چاپ میکند.
در گولنگ یکسری دیتا تایپ وجود دارند که به صورت پیش فرض دارای مقدار nil هستند مگر اینکه به آنها حافظه تخصیص بدهیم. برای مثال نوع های map , slice و هر نوع داده ای که پوینتر (pointer) باشد از این دسته هستند.
تابع Unmarshal در هنگام ذخیره کردن اطلاعات درون نوع دادهی رفرنس, در صورتی که حافظه ای به نوع داده ها تخصیص نداده شده باشد, به آنها حافظه تخصیص میدهد.
در مثال بالا, تابع Unmarshal وقتی دادهی JSON رو به ساختار IncomingMessage تبدیل میکند، فقط به قسمتهایی از ساختار حافظه تخصیص میدهد (allocate memory) که واقعاً در دادهی JSON وجود دارند.
محتوای این پست برگرفته شده از پست اصلی در مورد JSON در سایت رسمی گولنگ است. برای اطلاعات تکمیلیتر میتوانید راجب پکیج json و jsonrpc نیز مطالعه کنید.
قسمت قبل: Defer , Panic و Recover | گولنگ به زبان ساده
قسمت بعد: تبدیل داده ها به باینری با استفاده از Gob | گولنگ به زبان ساده