13.7. دستورالعملهای مدلسازی دادهها
نیکس در پوستههای مختلف، دادههای JSON و مجموعه ویژگیها را دریافت و تولید میکند. این دستورالعملها، رویههای سازگاری را برای رابطهای ما تضمین میکنند تا استفاده از آنها آسانتر شده و تجربه کار در یک بخش، به بخشهای دیگر نیز منتقل شود.
برای این دستورالعملها، ما از اصطلاحات JSON استفاده خواهیم کرد، اما این موارد به همان اندازه برای رابطهای جدید مجموعه ویژگی (primops و غیره) نیز صدق میکنند. توجه داشته باشید که این موارد در وهلهی اول دستورالعمل هستند. استثناها شامل موارد زیر میشوند:
- تست قابلیت: به عنوان مثال، نوشتن
builtins?frobnicateمشکلی ندارد. - سازگاری: ما بهطور کلی رابطهای پایدار را صرفاً جهت انطباق تغییر نمیدهیم. جایگزینهای جدید را میتوان با دقت اضافه کرد.
قابلیت گسترش
طناد (schema) ورودی و خروجی JSON باید امکان گسترش سازگار با گذشته (backwards compatible) را فراهم کند. این بخش نحوه دستیابی به این هدف را توضیح میدهد.
در اینجا دو تعاریف مفید هستند، زیرا در حالی که JSON تنها یک نوع شیء «کلید-مقدار» را تعریف میکند، ما از آن برای پوشش دادن دو مورد استفاده (use-case) استفاده میکنیم:
دیکشنری (dictionary): نگاشتی از نامها به مقادیری که همگی دارای یک نوع هستند. در C++ این معادل یک
std::mapبا کلیدهای رشتهای خواهد بود.رکورد (record): مجموعه ثابتی از صفتها که هر کدام نوع خاص خود را دارند. در C++، این مورد با یک
structنشان داده میشود.
بهتر است این موارد استفاده را با هم ترکیب نکنید، زیرا ممکن است هنگام تغییر طناد (schema) منجر به ناسازگاری شود. به عنوان مثال، افزودن یک فیلد رکورد به یک دیکشنری، مصرفکنندگانی را که فرض میکنند تمام فیلدهای شیء JSON دارای معنی و نوع یکسانی هستند، مختل میکند و اقلام دیکشنری با نام تداخلدار دیگر قابل نمایش نخواهند بود.
این امر به دستورالعملهای زیر منجر میشود:
مقدار سطح بالا (root) باید یک رکورد باشد.
در غیر این صورت، نمیتوان ساختار خروجی یک دستور را تغییر داد.
مقدار یک قلم دیکشنری باید یک رکورد باشد.
در غیر این صورت، نوع قلم قابل گسترش نخواهد بود.
اقلام لیست باید رکورد باشند.
در غیر این صورت، نمیتوان ساختار اقلام لیست را تغییر داد.
اگر ترتیب اقلام اهمیتی ندارد و هر قلم دارای یک کلید منحصربهفرد از نوع رشته است، به جای آن، نمایش لیست به صورت یک دیکشنری را در نظر بگیرید. اگر ترتیب اقلام باید حفظ شود، لیستی از رکوردها را برگردانید.
جریانسازی (streaming) دادههای JSON باید رکوردها را برگرداند.
نمونهای از قالب JSON جریانسازی، JSON lines است که در آن هر خط نشاندهندهی یک مقدار JSON است. این مقادیر JSON را میتوان به عنوان مقادیر سطح بالا یا اقلام لیست در نظر گرفت و آنها باید رکورد باشند.
مثالها
این مورد نامناسب است، زیرا باید فرض شود که تمام کلیدها از نوع انبار هستند:
{
"local": { ... },
"remote": { ... },
"http": { ... }
} این خوب است، زیرا در ریشه قابل توسعه است و تا حدودی مستندات خود را به همراه دارد:
{
"storeTypes": { "local": { ... }, ... },
"pluginSupport": true
} اگرچه در نگاه اول دیکشنری انواع انبار پاسخ بسیار کاملی به نظر میرسد، اما ممکن است مورد استفادهای پیش بیاید که بازگرداندن اطلاعات اضافی را توجیه کند. به عنوان مثال، وجود پشتیبانی از پلاگین ممکن است اطلاعات حیاتی برای ادامه کار یک کاربر (Client) در صورت عدم وجود نوع انبار مورد نظرشان باشد.
نمایندگی زیر بدلیل قابلیت توسعه نداشتن، نامناسب است:
{ "outputs": [ "out" "bin" ] } با این حال، صرفاً تبدیل کردن همه چیز به رکوردها کافی نیست، زیرا ترتیب خروجیها باید حفظ شود:
{ "outputs": { "bin": {}, "out": {} } } اولین مورد، خروجی پیشفرض است. استخراج این اطلاعات از ترتیب خروجیها چندان عالی نیست، اما در حال حاضر Nix به همین شکل کار میکند. اگرچه برای یک تجزیهکنندهی JSON حفظ ترتیب فیلدها امکانپذیر است، اما نمیتوانیم روی وجود این قابلیت در تمامی کتابخانههای JSON حساب باز کنیم.
این نمایش قابل توسعه است و ترتیب را حفظ میکند:
{ "outputs": [ { "outputName": "out" }, { "outputName": "bin" } ] } مقادیر خودتوصیف
همانطور که در بخش قبل توضیح داده شد، بسیار حیاتی است که اسکیماها بتوانند با فیلدهای جدید توسعه پیدا کنند بدون اینکه سازگاری از بین برود.
با این حال، این نباید به این معنا باشد که ما از وجود یا عدم وجود فیلدها برای نشان دادن اطلاعات اختیاری درون یک نسخه از اسکیما استفاده کنیم.
در عوض، همیشه فیلد را وارد کنید و از null برای نشان دادن حالت «هیچ» استفاده کنید.
مثالها
در اینجا دو شیء JSON آورده شده است:
{
"foo": {}
} {
"foo": {},
"bar": {}
} از آنجا که این موارد در فیلدهایی که شامل میشوند با یکدیگر تفاوت دارند، نباید هر دو مقادیر معتبری برای یک طرحواره (schema) یکسان باشند.
حداکثر، آنها میتوانند با دو طرحوارهی مختلف مطابقت داشته باشند که در آن دومی (دارای foo و bar) نسخهی جدیدتری از اولی (فقط با foo) در نظر گرفته میشود.
در هر نسخه، تمام فیلدها اجباری هستند (همیشه foo، و همیشه هم foo و هم bar).
فقط بین هر نسخه، bar به عنوان یک فیلد اجباری جدید اضافه میشود.
در اینجا دو شیء JSON دیگر آورده شده است:
{ "foo": null } { "foo": { "bar": 1 } } از آنجا که هر دوی آنها حاوی یک فیلد foo هستند، میتوانند مقادیر معتبری برای یک طرحواره (schema) واحد باشند.
این طرحواره فیلد foo را به عنوان یک فیلد اختیاری خواهد داشت که یا null است یا یک شیء (object) که در آن bar یک عدد صحیح است.