abox.tools

اللغة: العربية

كيف تنسّق JSON دون أن تناوله لأحد

تنسيقُ JSON ينبغي أن يغيّر المسافات البيضاء ولا شيء غيرها. ومعظمُ الأدوات التي تعرض فعلَ ذلك تغيّر أكثر من ذلك، ولا واحدةٌ منها تذكر الأمر. وهذا ما تنتبه له، وكيف تقرأ الخطأ حين يرفض الملفُّ أن يُحلَّل، ولماذا يستحقّ المربّعُ الذي تلصق فيه ملفَّ إعدادات أن يُفكَّر فيه.

افتح منسِّق JSON JSON وXML وHTML وCSS وYAML، منسَّقة أو محوَّلة. ولا شيء يُلصَق في خادم أحد آخر.

عرض بصيغة Markdown

الجواب القصير

افتح النصّ والشيفرة، والصق JSON في المربّع، واقرأه. فالترتيبُ يحدث وأنت تكتب، واللغةُ تُستنتَج من النصّ، والإزاحةُ مسافتان إلا أن تقول غير ذلك. ولا شيء يُرفَع، لأنه ليس ثمّة موضعٌ يذهب إليه: فالمحلّلُ بضعُ مئات من أسطر JavaScript تعمل في اللسان المفتوح عندك أصلاً.

وكلُّ ما دون ذلك هو الجزءُ الجدير بالمعرفة قبل أن تلصق ملفَّ إعدادات في أيٍّ من البدائل: ما الذي يُسمَح للمنسّق بتغييره، وما الذي يغيّره معظمُها على أي حال، وكيف تقرأ الخطأ حين يرفض الملفُّ أن يُحلَّل أصلاً.

لوحتان: سطر واحد من JSON على اليسار، والمستند نفسه منسّقًا بمسافتي إزاحة على اليمين.
سطر يدخل، وشيء مقروء يخرج. ولم يُرسل شيء إلى أي مكان لأجل ذلك.

ما التنسيق، وما ليس هو

ليس لـ JSON صياغةٌ تُذكَر. كائنٌ ومصفوفةٌ وسلسلةٌ ورقمٌ والكلماتُ الثلاث true وfalse وnull. وبين تلك القطع، لا تعني المسافةُ البيضاء شيئاً: فالملفُّ

{"name":"thing","tags":["local","offline"]}

والملفُّ

{
  "name": "thing",
  "tags": [
    "local",
    "offline"
  ]
}

هما المستندُ نفسه. والتنسيقُ هو شأنُ الانتقال من الأول إلى الثاني، وذلك هو العملُ كلُّه. وكلُّ ما عداه يفعله منسّقٌ بملفك — من إعادة ترتيب أو تقريب أو إسقاط — تغييرٌ لما يقوله المستند، مفعولٌ دون أن يُطلَب.

وثلاثةٌ من تلك التغييرات شائعةٌ بما يكفي لتُسمّى، لأنها صامتة ولأنها ما يفعله افتراضياً منسّقٌ كُتِب في عصر يوم.

الأشياءُ الثلاثة التي لا يجوز للمنسّق أن يغيّرها

ترتيبُ مفاتيحك

وهذه هي التي توقع بالناس. فالطريقةُ البيّنة لكتابة منسّق JSON بلغة JavaScript هي استدعاءُ JSON.parse ثم JSON.stringify بإزاحة، وذلك الزوجُ لا يحفظ ترتيب المفاتيح التي تبدو أعداداً صحيحة:

Object.keys(JSON.parse('{"10":"a","2":"b","x":"c"}'))
// ['2', '10', 'x']

وليس ذلك عطباً في شيفرة أحد. فكائناتُ JavaScript موصَّفةٌ بأن تضع المفاتيح الشبيهةَ بالأعداد الصحيحة أولاً، بترتيب عددي تصاعدي، وكلُّ قيمة تمرّ عبر JSON.parse تصير كائنَ JavaScript. فالمنسّقُ المبنيُّ هكذا سيعيد ترتيب ملف مفتاحُه معرِّفٌ أو رقمُ منفذ أو سنة أو رمزُ حالة HTTP، وسيفعل ذلك دون كلمة.

وهل يهمّ يتوقّف على الملف. فكائناتُ JSON غيرُ مرتَّبة مبدئياً، فلا شيء مكسورٌ تقنياً — لكنّ الفرق مع النسخة في مستودعك سيكون هائلاً، والمراجعةُ ستكون غيرَ مقروءة، وإن كان شيءٌ في المصبّ يقرأ الملف بالترتيب، فالسلوكُ يتغيّر.

أرقامُ أعدادك

لا تقول JSON كم يجوز أن يكون العددُ كبيراً، وJavaScript تقول: فكلُّ عدد مزدوجُ الدقّة. فالمنسّقُ الذي يحلّل إلى مزدوج ويطبعه من جديد يفقد كلَّ ما لا يستطيع المزدوجُ حملَه.

JSON.stringify(JSON.parse('{"id":123456789012345678901}'))
// {"id":123456789012345680000}

JSON.stringify(JSON.parse('{"size":1e999}'))
// {"size":null}

فمعرِّفٌ من واحد وعشرين رقماً — معرِّفُ Twitter، أو معرِّفُ Snowflake، أو مرجعٌ مصرفي — يعود عدداً آخر، والقيمةُ الأكبرُ من أن يحملها مزدوجٌ تعود null. وكلا الملفين ما زال يُحلَّل، ولا واحدٌ منهما الملفُّ الذي بدأت به.

والمخرجُ ألا تحلّل الأعداد أصلاً. فالمنسّقُ لا يحتاج إلا معرفةَ أين يبدأ العددُ وأين ينتهي ليرتّب المستند؛ ولا يحتاج قيمته أبداً، فالآمنُ أن تُنسَخ الأرقامُ كما كُتِبت بالضبط. وذلك ما تفعله الأداةُ هنا.

مفاتيحُك المكرَّرة

{"a": 1, "a": 2} صحيحٌ في JSON، والمعيارُ يمتنع عن قول أيِّهما يفوز. والمحلّلاتُ تختلف عملياً: فمعظمُها يبقي على الأخير، وبعضُها يبقي على الأول، وقليلٌ منها يرفض المستند. والمنسّقُ الذي يُخرِج أحدهما بصمت قد اتّخذ ذلك القرار نيابةً عنك، وأخفى الواقعةَ الأنفعَ كثيراً وهي أنهما كانا اثنين — وذلك خطأٌ في الملف في الغالب الأعمّ، وخطأٌ كنت ستريد رؤيته.

حين يرفض الملفُّ أن يُحلَّل

معظمُ JSON الذي يفشل ليس غريباً. بل هو واحدٌ من نحو ستة أشياء، والخطأُ يقول لك أيُّها إن قال أين هو بعبارة تستطيع إيجادها. والإزاحةُ مثل position 4193 ليست كذلك؛ أما السطرُ والعمود فنعم.

وواحدٌ ليس خطأً ويبدو خطأً: ملفٌّ يبدأ بعلامة ترتيب البايتات. فهي غيرُ مرئية في معظم المحرِّرات، وليست مسافةً بيضاء، وتجعل أولَ محرف في المستند غيرَ متوقَّع. فإن كان الخطأ عند السطر 1 والعمود 1 في ملف يبدو مثالياً، فذلك هو.

الأداة نفسها مع مستند معطوب: رسالة خطأ تسمّي سطر الفاصلة الزائدة وعمودها، ولوحة الإدخال تعرض السطر المخطئ.
حين لا يُقرأ الملف، تقول الرسالة أين الخلل. والفاصلة الزائدة أشيع الأسباب وأصعبها على العين.

التصغير، وكم قليلٌ ما يكسبه عادةً

عصرُ المسافات البيضاء العمليةُ نفسها معكوسة، ويجدر أن نكون واقعيين فيما يشتريه. فالمسافةُ البيضاء شديدةُ التكرار، وكلُّ خادم ومتصفح بينك وبين قارئ يضغط الاستجابة أصلاً بـ gzip أو Brotli، وهو بارعٌ جداً في ذلك النوع من التكرار بالضبط.

فـ JSON المصغَّر كثيراً ما يكون أصغرَ بثلاثين في المئة ملفاً وأصغرَ ببضعة في المئة على السلك لا غير. والمواضعُ التي يستحقّ فيها أجره هي التي لا ضغط أمامها: قيمةٌ في عمود قاعدة بيانات، أو حقلٌ في سطر سجلّ، أو حمولةٌ داخل رمز QR، أو مستندٌ توشك أن ترمّزه بـ Base64 في ترويسة.

وما يكلّفه هو الوضوح، وإن كان الملفُّ مودَعاً في مستودع فهو يكلّفك الفروق كذلك — فالملفُّ ذو السطر الواحد يتغيّر كلُّه كلما تغيّر أيُّ شيء فيه. فصغّر في طريق الخروج من محرِّرك، لا في طريق الدخول.

ترتيبُ المفاتيح، ومتى لا تفعل

ترتيبُ مفاتيح كل كائن معروضٌ هنا خياراً لا مطبَّقاً افتراضياً، لأنه تغييرٌ حقيقي في الملف وقيمتُه تتوقّف كلَّ التوقّف على ما توشك أن تفعله.

فهو يساعد حين تقارن مستندين ينبغي أن يقولا الشيء نفسه — إعداداتُ بيئتين، أو استجابةُ واجهة برمجية قبل تغيير وبعده — وأحدُهما يسرد مفاتيحه بترتيب آخر. فترتيبُ الاثنين أولاً يحوّل فرقاً في كل شيء إلى فرق في السطرين المختلفين فعلاً.

وهو يضرّ حين كان الترتيبُ يفعل شيئاً. فلملف package.json أعرافٌ فيما يأتي أولاً؛ والإعداداتُ المكتوبة باليد كثيراً ما تجمّع الإعدادات المتصلة؛ والملفُّ الذي رتّبت أداةٌ مفاتيحه ثم أُودِع ينتج إيداعاً واحداً هائلاً بلا معنى. فرتّب نسخةً لا الأصل.

وتفصيلةٌ جديرة بالمعرفة: الترتيبُ هنا بحسب كيف تُقرأ المفاتيح لا بحسب نقاط ترميزها، فـ item2 يأتي قبل item10 لا بعده. والترتيبُ بنقطة الترميز هو ما يضع item10 في وسط الآحاد، وهو صحيحٌ تقنياً وعديمُ النفع لقارئ.

مقارنةُ ملفَّي JSON

الطريقةُ الموثوقة أن تنسّق الاثنين التنسيق نفسه أولاً. فمستندان يقولان الشيء نفسه قد يختلفان في كل سطر إن كان أحدُهما مصغَّراً والآخرُ ليس كذلك، ولا يستطيع أيُّ فرق النظرَ من وراء ذلك.

فإذن: نسّق الأول، ونسّق الثاني، ثم قارن النتيجتين. والخطواتُ الثلاث كلُّها على الصفحة نفسها هنا — ولسانُ المقارنة يشارك المربّع مع التنسيق لهذا السبب بالضبط. وإن كانا يسردان مفاتيحهما بترتيبين مختلفين كذلك، فرتّب الاثنين وأنت تنسّقهما فتنهار المقارنةُ إلى الفرق الذي كنت تبحث عنه.

الجزءُ الذي لا يضعه أحدٌ على الصفحة

ابحث عن منسّق JSON فتجد عشرات المواقع عليها مربّع. واللصقُ في ذلك المربّع رفعٌ. فما كان في حافظتك — استجابةُ واجهة برمجية فيها عنوانُ عميل، أو ملفُّ إعدادات فيه سلسلةُ اتصال، أو رمزُ وصول كنت تنقّحه — قد أُرسِل إلى جهاز لا تتحكّم فيه، وهو الآن ملفُّ سجلّهم، وتقريرُ أخطائهم، ونسختُهم الاحتياطية.

وليس هذا افتراضاً عن سوء نية. فالموقعُ الحسنُ النية تماماً يحتفظ رغم ذلك بسجلات الوصول، ويشغّل رغم ذلك تحليلات، وله رغم ذلك مزوّدُ استضافة. فأأمنُ البيانات ما لم يغادر قط، وفي عمل هو معالجةُ سلاسل بالكامل ليس ثمّة سببٌ البتة لأن يغادر.

وفحصان، وهما يعملان على أي موقع يدّعي هذا، لا على هذا وحده:

  1. افتح أدوات المطوِّر، وراقب لسان الشبكة، ونسّق شيئاً. فإن كان نصُّك يُرسَل، فثمّة طلبٌ يحمله. ولا شيء آخر يمكن أن يكون صادقاً في الوقت نفسه.
  2. اقطع الاتصال بالإنترنت وحاول من جديد. فالأداةُ التي تنجز العمل في متصفحك لا تتأثّر. والأداةُ التي ترسل نصّك إلى مكان ما تتوقّف عن العمل، فوراً وتماماً.

ولكليهما نسخةٌ أطول، مع فحصين آخرين، في هل رفع الملفات آمن.

وماذا عن YAML وXML وما بقي

الصفحةُ نفسها تقرأ XML وHTML وCSS وYAML، وتحوّل بين JSON والأولى والأخيرة منها. وشيئان جديران بالنقل ممّا سبق، لأنهما الحجّةُ نفسها في ثوب آخر: