JSON Toolkit

JSON إلى CSV: من أين يأتي الترويسة ولماذا تتغير القيم

الخلاصة: تسع تجارب على محوّل حقيقي: استراتيجية «مفاتيح السجل الأول» تفقد الحقول بصمت، وnull والغياب والسلسلة الفارغة تصبح جميعها خلايا فارغة، وعدد صحيح من 20 خانة يفقد دقته منذ JSON.parse، ونقطة داخل مفتاح تجعل مسارين مختلفين يتصادمان في العمود نفسه. كيف تُبنى الترويسة، وأي القيم تتشوه، وكيف تختار استراتيجية حسب الاستخدام اللاحق.

متقدمقراءة نحو 6 دقيقةنُشر في 2026-11-04حُدِّث في 2026-11-04
استراتيجيات الترويسة وأمانة القيم في تحويل JSON إلى CSV

حين يفقد تحويل JSON إلى CSV بيانات، فليس تقريبًا أبدًا في خطوة «التحويل» نفسها. الخسارة تحدث في سؤالين لم يسألهما أحد: من قرّر الترويسة، وهل تبقى كل خلية معترفًا بها كالقيمة التي كانت؟

حقل يختفي في ثلاثين ثانية

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

data = [{"id": 1, "name": "A"}, {"id": 2, "email": "b@x.com"}]
first_keys = list(data[0].keys())          # ['id', 'name']
rows = [[r.get(k, "") for k in first_keys] for r in data]
# بريد السجل الثاني يختفي بلا خطأ ولا تحذير

المفتاح email ليس في الترويسة: لا يملك حتى حق أن يصبح خلية فارغة — العمولم يُنشأ قط، والقيمة تُرمى بصمت. الإصلاح هو اتحاد المفاتيح: تجاوز كل السجلات وإنشاء عمود لكل مفتاح يظهر في أي مكان. البيانات نفسها عبر الاتحاد تنتج الترويسة id,name/email، مع خلية فارغة حيث يغيب name — ولا شيء يضيع.

المحوّل المباشر في صفحة /convert/csv لدينا يطبق هذه الاستراتيجية بالضبط. بإدخال [{"id":1,"name":"A"},{"id":2,"email":"b@x.com"}] ينتج:

id,name,email
1,A,
2,,b@x.com

تُرتب المفاتيح حسب أول ظهور، والحقول الغائبة تصبح خلايا فارغة. jsonoperations وjsonbeam كلتاهما تسمي هذا «إصلاح الخطأ الكلاسيكي» — لكن «يستخدم الاتحاد» نصف القصة فقط. للاتحاد ثمنه هو؛ وذلك هو القسم التالي.

ثلاث طرق لبناء ترويسة — ولكل منها من يناسبها

استراتيجية الترويسة كيف تُبنى القوة الثمن
مفاتيح السجل الأول لا يُقرأ إلا السجل الأول أعمدة قليلة، ثابتة، سطر jq واحد الحقول الجديدة في السجلات اللاحقة تضيع بصمت
اتحاد المفاتيح تُقرأ كل السجلات لا يضيع أي حقل أعمدة متناثرة وخلايا فارغة كثيرة حين تتفاوت الأشكال
قائمة أعمدة صريحة تُعرّف يدويًا أو بالمخطط قابلة للتحكم كليًا، قابلة لإعادة الإنتاج تحتاج صيانة؛ الحقول الجديدة تتطلب تحديثًا يدويًا

معيار الاختيار ليس «أيها أكثر تطورًا» بل ماذا يفعل المتلقّي بهذا CSV:

  • زميل يلقي نظرة: مفاتيح السجل الأول تكفي، فأشكال السجلات عادة متجانسة.
  • تقرير أو خط بيانات: اتحاد المفاتيح هو الحد الأدنى — الخلية الفارغة بذاتها معلومة.
  • تصدير آلي دوري: قائمة الأعمدة الصريحة. انحراف الشكل (حقل مضاف من المنبع، مفتاح أعيدت تسميته) يجب أن يفشل بصوت عال، لا أن ينتج بصمت جدولًا لا أحد ينتبه إلى خطئه.

الخيار الثالث هو الأكثر إهمالًا. في الأتمتة، «نجح التحويل» و«التحويل صحيح» قولان مختلفان — وقائمة الأعمدة الصريحة هي الآلية الوحيدة التي تفصلهما.

حين يحتوي المفتاح نقطة، تتصادم مسارات التسطيح

التسطيح بترقيم النقاط يحوّل {"a": {"b": 2}} إلى عمود a.b. لكن ماذا لو احتوى المفتاح نفسه نقطة؟ جرّبناه مع pandas:

import pandas as pd
d = [{"a.b": 1, "a": {"b": 2}}]
pd.json_normalize(d, sep=".")
# ينجو عمود واحد فقط: ['a.b'] — مفتاحان مختلفان تمامًا يتصادمان على المسار نفسه

المفتاح الحرفي a.b والمسار المتداخل a → b ينتجان اسم العمود نفسه، فأحدهما يطمس الآخر، وتفسد البيانات بصمت. مثال مصطنع، لكنه يحدث مع بيانات حقيقية: أسماء مقاييس المراقبة (cpu.usage) وحقول السجلات (error.type) تحب النقاط.

علاجان:

  1. غيّر الفاصل. sep="_" أو فاصلًا مخصصًا — بعد التحقق من أن المفاتيح الأصلية لا تحتويه أبدًا.
  2. غيّر ترميز المسارات. JSON Pointer يستخدم / فاصلًا (/a/b)؛ محوّل Excel من toolvore يسلك هذا الطريق. له الفئة نفسها من الغموض — مفتاح يحوي / يُكتب ~1 — لكن قواعد الترميز معيارية في RFC 6901 بدل أن يخترعها كل أداة لنفسها.

أيًّا كان اختيارك، اكتب القاعدة في توثيق التصدير. ترويسة CSV هي «المخطط» الوحيد الذي تحمله هذه البيانات؛ ولن يجد القارئ قواعد تسطيحك في أي مكان آخر.

المصفوفات: ثلاث استراتيجيات، وكل واحدة تفقد شيئًا مختلفًا

المصفوفات هي العائق الحقيقي في الطريق من JSON إلى CSV: الخلية الواحدة لا تستوعب قيمًا متعددة. ثلاث استراتيجيات، بخسائر تم التحقق منها عمليًا:

الاستراتيجية شكل المخرجات ما الذي يضيع مثالية لـ
JSON داخل الخلية تُسلسَل المصفوفة كسلسلة JSON داخل الخلية أدوات الجداول لا تستطيع تصفية العناصر ولا تجميعها الأمانة أولًا، المعاينة اليدوية
أعمدة مفهرسة tags.0, tags.1, عمود لكل منها عدد الأعمدة ينفجر مع أطول مصفوفة؛ فراغات كثيرة مصفوفات صغيرة بطول ثابت
صفوف مفجّرة صف لكل عنصر، حقول الأب مكررة تضخم الصفوف؛ سلوك المصفوفات الفارغة يلزم تعريفه على حدة تحليل التفاصيل، التجميع لكل عنصر

محوّلا /convert/csv و/convert/table لدينا يستخدمان JSON داخل الخلية. الإدخال [{"orderId":"A001","items":[{"sku":"X1","qty":2},{"sku":"Y9","qty":1}]}] ينتج:

orderId,items
A001,"[{""sku"":""X1"",""qty"":2},{""sku"":""Y9"",""qty"":1}]"

حين تحتاج صفوفًا مفجّرة، يقوم بذلك record_path في pandas مباشرة:

orders = [{"orderId": "A001", "total": 99,
           "items": [{"sku": "X1", "qty": 2}, {"sku": "Y9", "qty": 1}]}]
items = pd.json_normalize(orders, record_path=["items"],
                          meta=["orderId", "total"], sep=".")
# صفان تفصيليان؛ orderId وtotal مكرران في كل منهما

في التفجير ثلاث فخاخ يلزم حسمها مسبقًا: تفجير عدة مصفوفات معًا يبني جداءً ديكارتيًا؛ والمصفوفات الفارغة لا تنتج صفًا واحدًا افتراضيًا (قائمة عملائك تخسر أشخاصًا)؛ وحقول الأب المكررة تتطلب إزالة التكرار عند التجميع بـ orderId في المصب. يضع دليل convertcsvonline «قرر مستوى التفاصيل أولًا» خطوة أولى لهذا السبب بالضبط — ما إن يثبت مستوى التفاصيل، يكون لكل سؤال من الأسئلة الثلاثة إجابة واحدة.

مُختبَر: قيم تخرج من التحويل وقد تغيّرت

اختبار أمانة أُجري على محوّلنا الحقيقي (Node v22.13.1 + papaparse، التنفيذ نفسه المعتمد في /convert/csv):

1. null والغياب والسلسلة الفارغة تصبح جميعها خلايا فارغة. إدخال [{"a":null},{"b":""},{"c":1}] — تخرج الخلايا الثلاث فارغة. لا يملك CSV ثلاث حالات، بل «توجد محارف» و«لا توجد محارف». بعد رحلة ذهاب وعودة تُقرأ الثلاثة ""؛ والمعنى الأصلي غير قابل للاسترجاع.

2. الكائنات والمصفوفات المتداخلة تصبح سلاسل JSON. تُضاعف علامات الاقتباس وفق RFC 4180 ({""name"":""张三""})، وبمجرد لصق الخلية في أي محلل JSON تعود القيمة — ولهذا يحافظ JSON داخل الخلية على الأمانة.

3. عدد صحيح من 20 خانة يفقد دقته منذ لحظة التحليل — واللوم ليس على CSV. إدخال "id": 9007199254740993؛ وفي CSV وصل بالفعل 9007199254740992. يستخدم JSON.parse في جافاسكربت double وفق IEEE 754: الفقد يقع عند التحليل، قبل أي محوّل CSV. يحفظه Python json.load كاملًا. حقول المعرّفات (معرفات snowflake، أرقام الطلبات) ينبغي أن تصل سلاسل من المنتج أصلًا. المزيد عن دقة الأرقام في لماذا تفقد الأعداد الكبيرة دقتها.

4. الأصفار البادئة في السلاسل آمنة في طبقة نص CSV. "phone": "00123" تبقى 00123 في CSV، لأنها سلسلة من البداية إلى النهاية ولا شيء يخدشها. الذي يخدش الأصفار خطوة لاحقة حين يعامل Excel الخلية كعدد — انظر أربع طرق لتحويل JSON إلى Excel.

5. الحقول ذات الفواصل وعلامات الاقتباس أو فواصل الأسطر. يلتزم papaparse بـ RFC 4180: اقتبس الحقل، ضاعف الاقتباسات الداخلية. "He said \"hi\", ok" يخرج "He said ""hi"", ok" ويعبر الذهاب والعودة دون خدش. المُسلسِلات المكتوبة يدويًا التي تتحايل على مكتبة تفشل شبه دائمًا في هذا الموضع تحديدًا.

الذهاب والعودة: ما يغيّره مفتاح الاستدلال

يجب على CSV → JSON أن يخمّن نوع كل خلية. محوّلنا يطفئ الاستدلال افتراضيًا ويعيد كل شيء سلاسل:

[{ "id": "9007199254740992", "phone": "00123" }]

مع تفعيل typeInference يصبح id عددًا (الدقة قد فُقدت أصلًا) بينما يبقى phone سلسلة — تستبعد قواعد الاستدلال الأشكال ذات الأصفار البادئة مثل 010 لأنها ليست أعدادًا قانونية، فتنجو السلسلة بحسن الحظ.

احفظ هذه اللاتماثل: الاستدلال المطفي في أسوأ الأحوال يكلفك الأنواع؛ والاستدلال المفعل قد يكلفك البيانات. الأعمدة التي تحتاج أعدادًا تُحوَّل بتعريفات أعمدة صريحة، لا تُودَع لاستدلال شامل. تنفيذ rowsToRecords في مقالتنا السابقة JSON وCSV والبيانات الجدولية هو هذا المبدأ نفسه في صورة شيفرة.

اختيار الاستراتيجية حسب المتلقّي: جدول قرار

من يتلقى CSV استراتيجية الترويسة التعامل مع المصفوفات الأنواع
زميل يفتحه مرة واحدة اتحاد المفاتيح JSON داخل الخلية بلا استدلال
Excel / التقارير اتحاد المفاتيح JSON داخل الخلية، أو أعمدة مفهرسة إن كان الطول ثابتًا أعمدة المعرّفات فرضًا نصًا
استيراد إلى قاعدة بيانات قائمة أعمدة صريحة جداول master/detail منفصلة، أو تفجير تعريفات أعمدة صريحة
BI / تجميع قائمة أعمدة صريحة صفوف مفجّرة، مستوى التفاصيل = أصغر وحدة تحليل تعريفات أعمدة صريحة
خط آلي قائمة أعمدة صريحة JSON داخل الخلية، تفجير في المصب استدلال مطفي، تحويل عمودًا عمودًا

القانون المشترك: كلما اقتربت من الإنسان سمحت لنفسك بالتخفيف أكثر؛ وكلما اقتربت من الآلة وجب أن يكون كل شيء صريحًا.

قائمة تحقق للنسخ

  • [ ] هل تأتي الترويسة من اتحاد كامل للمفاتيح أم من قائمة صريحة؟ إن لم يكن، فهل تستطيع إثبات أن أي سجل لم يفقد حقلًا؟
  • [ ] هل يمكن أن تحتوي المفاتيح نقاطًا أو شرطات مائلة؟ هل فُحص التصادم مع فاصل التسطيح؟
  • [ ] هل استراتيجية المصفوفات موثقة؟ هل حُسم سلوك المصفوفات الفارغة؟
  • [ ] عن null والغياب والسلسلة الفارغة: ماذا يقرأ المتلقّي من كل منها؟ اختر قاعدة وثبّتها.
  • [ ] هل تسافر أعمدة المعرّفات (المعرفات، أرقام الطلبات، الهواتف) سلاسل من أول الرحلة إلى آخرها؟
  • [ ] بعد التفجير، هل يساوي عدد الصفوف مجموع أطوال المصفوفات؟ هل فُحص بالعينة؟
  • [ ] هل تنجو حقول العينة ذات الفواصل وعلامات الاقتباس وفواصل الأسطر عبر الذهاب والعودة دون خدش؟
  • [ ] هل الاستدلال الشامل للأنواع مطفي عند إعادة الاستيراد، مع تعريفات صريحة للأعمدة العددية؟

فقط حين تكون للثمانية إجابات واضحة يكون هذا CSV «صحيحًا» — لا مجرد «حُوّل بنجاح».

FAQ

لماذا لا يُسطّح المحوّل الكائنات المتداخلة إلى أعمدة افتراضيًا؟

التسطيح يخرج عدد الأعمدة عن السيطرة: التداخل العميق ينتج أسماء مثل user.address.geo.lat، ومدمجًا مع اتحاد المفاتيح يكفي سجل واحد بشكل مختلف لإضافة كومة من الأعمدة. سلاسل JSON داخل الخلايا تحفظ الأمانة وعدد أعمدة ثابتًا؛ أسطّح صراحةً متى احتجت.

ماذا أفعل إذا تضمنت الترويسة أسماء أعمدة مكررة؟

تصادم مفتاحين مختلفين بعد التسطيح (مثل a.b مقابل a→b المتداخل) يُحل في المنبع: غيّر الفاصل أو انتقل إلى JSON Pointer. وللتكرار داخل الكائن نفسه، تحتفظ المحوّلات عادة بالقيمة الأخيرة؛ أما التكرار في الترويسة فيعتمد على التنفيذ — unparse في papaparse لا يزيل التكرار، وExcel عند الفتح يدمج المكرر أو يرميه بصمت. لهذا هو في قائمة التحقق.

حقول الأب المكررة بعد التفجير — أهي خطأ برمجي؟

لا — هذا تصميم. الصف المفجّر صف تفاصيل، وصف التفاصيل يجب أن يعود بالربط إلى سجل الأب بالمفتاح. جمّع في المصب بمفتاح الأب؛ وإن كان الحجم مقلقًا، صدّر ملف التفاصيل وجدول master منفصلًا.

كيف أتحقق أن تحويلي لم يفقد أي حقل؟

أجرِ تدقيقًا للمفاتيح: احسب اتحاد كل مفاتيح JSON المصدر، ثم مجموعة ترويسات أعمدة CSV، وخذ الفرق. فرق غير فارغ يعني حقولًا مفقودة. في الخطوط الدفعية، رمّز ذلك كـ assert في اختباراتك: أي تبديل لمحوّل يبدأ في فقدان حقول سيفشل تلقائيًا.