جیسان (JSON) | گولنگ به زبان ساده
۲۰ مهر ۱۴۰۴
گولنگ مانند باقی زبان های برنامه نویسی از JSON پشتیبانی میکند. در اینجا با نحوه استفاده از جیسان در برنامه های گولنگی آشنا میشیم.
جیسان (JSON) یا JavaScript Object Notation، یک فرمت ساده و قابلخواندن برای تبادل دادهها است. از لحاظِ ساختارِ ظاهری, داده ها در JSON شبیه اشیا و یا آرایه ها در جاوا اسکریپت هستند. تقریبا تمام زبانهای برنامه نویسی از 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 به فرمت قابل استفاده در برنامه
برای دیکد کردن 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) دارای ساختار ناشناخته یا متغیر
بدون آنکه ساختار دادهی 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) چاپ میکند.
نوع دادهی رفرس (Reference Type)
در گولنگ یکسری دیتا تایپ وجود دارند که به صورت پیش فرض دارای مقدار nil
هستند مگر اینکه به آنها حافظه تخصیص بدهیم. برای مثال نوع های map
, slice
و هر نوع داده ای که پوینتر (pointer) باشد از این دسته هستند.
تابع Unmarshal
در هنگام ذخیره کردن اطلاعات درون نوع دادهی رفرنس, در صورتی که حافظه ای به نوع داده ها تخصیص نداده شده باشد, به آنها حافظه تخصیص میدهد.
در مثال بالا, تابع Unmarshal
وقتی دادهی JSON رو به ساختار IncomingMessage
تبدیل میکند، فقط به قسمتهایی از ساختار حافظه تخصیص میدهد (allocate memory) که واقعاً در دادهی JSON وجود دارند.
محتوای این پست برگرفته شده از پست اصلی در مورد JSON در سایت رسمی گولنگ است. برای اطلاعات تکمیلیتر میتوانید راجب پکیج json و jsonrpc نیز مطالعه کنید.
قسمت قبل: Defer , Panic و Recover | گولنگ به زبان ساده
قسمت بعد: به زودی…