دليل تحويل CSV إلى JSON: الطرق والمزالق وأفضل الممارسات
يرسل لك فريق العمليات ملف CSV مُصدَّر. واجهة API لديك تتوقع JSON. تفتح الملف وتحدق في 10,000 صف من القيم المفصولة بفواصل، وتتساءل: ما أسرع طريقة لتحويل CSV إلى JSON دون فقدان البيانات؟
يغطي هذا الدليل أربع طرق للتحويل (أدوات المتصفح، JavaScript، Python، سطر الأوامر)، والاتجاه العكسي (JSON إلى CSV)، وخمسة مزالق تُفسد بياناتك بصمت، وكيفية التعامل مع الملفات الكبيرة جداً للذاكرة.
CSV مقابل JSON: متى تستخدم كلاً منهما
قبل التحويل، من المفيد فهم ما يُجيده كل تنسيق.
| البُعد | CSV | JSON |
|---|---|---|
| البنية | جدول مسطح (صفوف وأعمدة) | تسلسل هرمي متداخل (كائنات، مصفوفات) |
| أنواع البيانات | كل شيء سلسلة نصية | سلسلة نصية، رقم، قيمة منطقية، null |
| سهولة القراءة | مناسب لجداول البيانات | مناسب للمطورين |
| الاستخدام الأساسي | تصدير/استيراد البيانات، التقارير، ETL | واجهات API، ملفات التكوين، تخزين NoSQL |
| حجم الملف | أصغر (لا تكرار لأسماء المفاتيح) | أكبر (تكرار أسماء المفاتيح لكل سجل) |
| المخطط | ضمني (صف الرأس) | صريح (أو استخدام JSON Schema) |
القاعدة العامة: استخدم CSV عندما تكون بياناتك جدولية والمستهلك هو جدول بيانات أو خط أنابيب بيانات. استخدم JSON عندما تحتوي بياناتك على تسلسل هرمي أو يكون المستهلك واجهة API. يمكنك دائماً التحقق من مخرجات JSON باستخدام منسق JSON لاكتشاف المشكلات الهيكلية مبكراً.
إذا كان مشروعك يستخدم تنسيقات JSON المرنة مثل JSON5 أو JSONC للتكوين، راجع دليل تنسيق JSON5 وJSONC للاطلاع على الفروقات في الصياغة والأدوات.
4 طرق لتحويل CSV إلى JSON
الطريقة 1 — أداة المتصفح
للتحويلات لمرة واحدة، النهج القائم على المتصفح هو الأسرع. الصق CSV في محول عبر الإنترنت، واحصل على JSON، ثم تحقق من النتيجة في منسق JSON للتأكد من صحة البنية.
الميزة: بياناتك لا تغادر متصفحك أبداً. لا رفع، لا معالجة على الخادم، لا مخاوف تتعلق بالخصوصية. هذا مهم عند التعامل مع بيانات داخلية أو مفاتيح API مضمنة في الملفات المصدرة، أو أي شيء تفضل عدم إرساله إلى خادم طرف ثالث.
الأفضل لـ: الملفات الصغيرة (أقل من 10 ميجابايت)، التحويلات السريعة لمرة واحدة، وأعضاء الفريق غير التقنيين.
الطريقة 2 — JavaScript / Node.js
المتصفح (JavaScript عادي):
function csvToJson(csv) {
const lines = csv.trim().split('\n');
const headers = lines[0].split(',').map(h => h.trim());
return lines.slice(1).map(line => {
const values = line.split(',');
return headers.reduce((obj, header, i) => {
obj[header] = values[i]?.trim() ?? '';
return obj;
}, {});
});
}
const csv = `name,age,city
Alice,30,New York
Bob,25,London`;
console.log(JSON.stringify(csvToJson(csv), null, 2));
هذا يعمل مع CSV البسيط بدون حقول بين علامات اقتباس. للاستخدام في الإنتاج مع فواصل داخل القيم أو أسطر جديدة في الحقول أو سلاسل نصية بين علامات اقتباس، استخدم محللاً مناسباً.
Node.js (csv-parser + التدفقات):
import { createReadStream } from 'fs';
import { parse } from 'csv-parse';
const records = [];
createReadStream('data.csv')
.pipe(parse({ columns: true, trim: true, skip_empty_lines: true }))
.on('data', (row) => records.push(row))
.on('end', () => {
console.log(JSON.stringify(records, null, 2));
});
خيار columns: true يستخدم الصف الأول كمفاتيح. خيار trim يزيل المسافات من القيم. هذا يتعامل بشكل صحيح مع الحقول المقتبسة والفواصل المهروبة والقيم متعددة الأسطر.
الطريقة 3 — Python
المكتبة القياسية (بدون تبعيات):
import csv
import json
with open('data.csv', encoding='utf-8') as f:
reader = csv.DictReader(f)
rows = list(reader)
with open('data.json', 'w', encoding='utf-8') as f:
json.dump(rows, f, indent=2, ensure_ascii=False)
csv.DictReader يربط كل صف بقاموس باستخدام صف الرأس كمفاتيح. علم ensure_ascii=False يحافظ على أحرف Unicode (الصينية، اليابانية، الأحرف المشكّلة) بدلاً من تحويلها إلى \uXXXX.
Pandas (سطر واحد لعلماء البيانات):
import pandas as pd
df = pd.read_csv('data.csv')
df.to_json('data.json', orient='records', indent=2, force_ascii=False)
متى تستخدم أيهما:
- csv + json: السكريبتات الخفيفة، دوال Lambda، الحاويات حيث تريد أقل تبعيات ممكنة.
- pandas: عندما تحتاج أيضاً لتنظيف أو تصفية أو تحويل البيانات قبل التحويل. تكلفة استيراد pandas تستحق عندما تفعل أكثر من مجرد تحويل التنسيق.
الطريقة 4 — أدوات سطر الأوامر
لسكريبتات الشل وأنابيب الأتمتة:
csvkit:
# Install: pip install csvkit
csvjson data.csv > data.json
Miller (mlr):
# Install: brew install miller (macOS) or apt install miller (Ubuntu)
mlr --csv --json cat data.csv > data.json
التوجيه مع jq للتصفية:
# Convert and filter in one pipeline
csvjson data.csv | jq '[.[] | select(.age | tonumber > 25)]'
Miller قوي بشكل خاص لأنه يتعامل مع CSV وJSON وTSV وتنسيقات أخرى أصلياً. يمكنك تحويل البيانات أثناء التحويل:
# Convert CSV to JSON, rename a field, add a computed field
mlr --csv --json rename name,fullName then put '$age_group = ($age > 30) ? "senior" : "junior"' data.csv
JSON إلى CSV: التعامل مع الاتجاه العكسي
تحويل JSON إلى CSV يطرح تحديات غير موجودة في الاتجاه الأمامي.
تسطيح الكائنات المتداخلة
CSV مسطح بطبيعته. عندما يحتوي JSON على كائنات متداخلة، تحتاج لاستراتيجية تسطيح:
{
"name": "Alice",
"address": {
"city": "New York",
"zip": "10001"
}
}
يصبح:
| name | address.city | address.zip |
|---|---|---|
| Alice | New York | 10001 |
اصطلاح التدوين بالنقطة (address.city) هو النهج الأكثر شيوعاً. في Python:
import pandas as pd
data = [
{"name": "Alice", "address": {"city": "New York", "zip": "10001"}},
{"name": "Bob", "address": {"city": "London", "zip": "EC1A"}}
]
df = pd.json_normalize(data)
df.to_csv('output.csv', index=False)
# Columns: name, address.city, address.zip
التعامل مع المصفوفات
حقول المصفوفات تتطلب قراراً:
| الاستراتيجية | مثال المدخلات | مخرجات CSV | الأفضل لـ |
|---|---|---|---|
| الدمج كسلسلة نصية | ["admin","editor"] | admin;editor | القوائم البسيطة، قابل لإعادة الاستيراد |
| التوسيع إلى أعمدة | ["admin","editor"] | role_0: admin, role_1: editor | المصفوفات ثابتة الطول |
| التوسيع إلى صفوف | ["admin","editor"] | صفان، واحد لكل دور | التحليل العلائقي |
اختر بناءً على المستهلك النهائي. إذا كان CSV سيعود إلى قاعدة بيانات، فالتوسيع إلى صفوف عادة هو الأنسب.
فقدان معلومات النوع
CSV ليس لديه نظام أنواع. عند تحويل JSON إلى CSV:
trueتصبح السلسلة النصية"true"— هل هي قيمة منطقية أم سلسلة نصية؟nullتصبح خلية فارغة — لا يمكن تمييزها عن سلسلة نصية فارغة""42تصبح"42"— هل هو رقم أم سلسلة نصية؟
إذا كانت دقة الرحلة ذهاباً وإياباً مهمة (CSV ← معالجة ← JSON)، وثّق اصطلاحات الأنواع في تعليق رأس أو ملف مخطط مرافق.
5 مزالق شائعة وكيفية تجنبها
هذه المشكلات تُفسد البيانات بصمت. معظم الدروس تتجاهلها. لا تتعلمها في بيئة الإنتاج.
1. ألغام الترميز
المشكلة: تفتح ملف CSV من زميل وترى é بدلاً من é، أو 锟斤拷 بدلاً من أحرف صينية.
السبب: تم حفظ الملف بترميز معين (Windows-1252، GBK، Shift_JIS) لكن المحلل يفترض UTF-8. غالباً ما يحفظ Excel على Windows ملفات CSV بترميز Windows-1252 أو يضيف علامة ترتيب البايتات UTF-8 BOM (الأحرف غير المرئية \xEF\xBB\xBF في بداية الملف).
الحل:
# Detect encoding first
import chardet
with open('data.csv', 'rb') as f:
result = chardet.detect(f.read(10000))
print(result) # {'encoding': 'Windows-1252', 'confidence': 0.73}
# Then read with the correct encoding
with open('data.csv', encoding=result['encoding']) as f:
reader = csv.DictReader(f)
# ...
في Node.js، أزل علامة BOM صراحة:
import { readFileSync } from 'fs';
let content = readFileSync('data.csv', 'utf-8');
// Strip UTF-8 BOM if present
if (content.charCodeAt(0) === 0xFEFF) {
content = content.slice(1);
}
2. ارتباك الفاصل
المشكلة: يُنتج المحلل عموداً عملاقاً واحداً بدلاً من حقول متعددة.
السبب: في العديد من الإعدادات الإقليمية الأوروبية (فرنسا، ألمانيا، إسبانيا)، يستخدم Excel الفواصل المنقوطة (;) كفاصل CSV لأن الفواصل تُستخدم كفواصل عشرية (مثل 3,14 بدلاً من 3.14). ملفات التبويب (.tsv) تضيف متغيراً آخر.
الحل: اكتشف الفاصل تلقائياً بأخذ عينة من الأسطر الأولى:
import csv
with open('data.csv') as f:
sample = f.read(8192)
dialect = csv.Sniffer().sniff(sample, delimiters=',;\t|')
f.seek(0)
reader = csv.DictReader(f, dialect=dialect)
3. اختفاء الأصفار البادئة
المشكلة: الرمز البريدي 00501 يصبح 501. رمز المنتج 007 يصبح 7.
السبب: يفسر المحلل (أو Excel) الحقل كرقم ويزيل الأصفار البادئة. هذا خطير بشكل خاص مع الرموز البريدية وأرقام الهواتف ورموز المعرفات.
الحل: فرض نوع السلسلة النصية. في pandas:
df = pd.read_csv('data.csv', dtype={'zip': str, 'product_code': str})
في JavaScript، تحقق مما إذا كانت السلسلة النصية الأصلية تختلف عن تحليلها الرقمي:
function preserveLeadingZeros(value) {
if (/^0\d+$/.test(value)) return value; // Keep as string
const num = Number(value);
return isNaN(num) ? value : num;
}
4. فقدان دقة الأرقام الكبيرة
المشكلة: المعرف 9007199254740993 يصبح 9007199254740992 في JSON.
السبب: Number في JavaScript هو عدد عشري 64 بت (IEEE 754). الأعداد الصحيحة فوق Number.MAX_SAFE_INTEGER (2^53 - 1 = 9007199254740991) تفقد الدقة. يؤثر هذا على معرفات قواعد البيانات ومعرفات Snowflake ومعرفات منشورات Twitter/X.
الحل: احتفظ بالأرقام الكبيرة كسلاسل نصية في JSON، أو استخدم BigInt في كود المعالجة:
// Parse with string preservation for large numbers
function safeParseNumber(value) {
const num = Number(value);
if (Number.isInteger(num) && !Number.isSafeInteger(num)) {
return value; // Keep as string to preserve precision
}
return isNaN(num) ? value : num;
}
5. غموض القيم الفارغة
المشكلة: يحتوي CSV على خلايا فارغة. بعد التحويل، لا يمكنك معرفة ما إذا كانت القيمة الأصلية سلسلة نصية فارغة ""، أو null، أو مفقودة ببساطة.
السبب: لا يملك CSV طريقة للتمييز بين هذه الحالات الثلاث. حقل فارغ بين فاصلتين (Alice,,30) قد يعني أياً منها.
الحل: حدد اصطلاحاً وطبقه باتساق:
def parse_value(value):
if value == '':
return None # or '' — pick one convention
if value == 'NULL' or value == 'null':
return None
return value
إذا كانت بياناتك تستخدم قيماً مميزة مثل NULL أو N/A أو -، وثّقها وتعامل معها صراحة.
بث الملفات الكبيرة
عندما يتجاوز ملف CSV حجم 100 ميجابايت، تحميله بالكامل في الذاكرة ليس خياراً. استخدم البث.
Node.js (خط أنابيب التدفق):
import { createReadStream, createWriteStream } from 'fs';
import { parse } from 'csv-parse';
import { Transform } from 'stream';
import { pipeline } from 'stream/promises';
let first = true;
const toJsonArray = new Transform({
objectMode: true,
transform(record, encoding, callback) {
const prefix = first ? '[\n' : ',\n';
first = false;
callback(null, prefix + JSON.stringify(record));
},
flush(callback) {
callback(null, '\n]');
}
});
await pipeline(
createReadStream('large.csv'),
parse({ columns: true, trim: true }),
toJsonArray,
createWriteStream('large.json')
);
Python (مولد):
import csv
import json
def csv_rows(path):
with open(path, encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
yield row
# Stream to JSON Lines format (one JSON object per line)
with open('large.jsonl', 'w', encoding='utf-8') as out:
for row in csv_rows('large.csv'):
out.write(json.dumps(row, ensure_ascii=False) + '\n')
للملفات الكبيرة جداً، فكر في تنسيق JSON Lines (.jsonl) بدلاً من مصفوفة JSON واحدة. كل سطر هو كائن JSON مستقل، مما يعني أنه يمكنك معالجة ملف المخرجات سطراً بسطر أيضاً — لا حاجة لتحليل الملف بأكمله.
الأسئلة الشائعة
ما الفرق بين CSV وJSON؟
CSV (القيم المفصولة بفواصل) يخزن البيانات كجدول مسطح حيث كل قيمة هي سلسلة نصية. JSON (ترميز كائنات JavaScript) يخزن بيانات منظمة مع كائنات متداخلة ومصفوفات وقيم مصنفة (سلاسل نصية، أرقام، قيم منطقية، null). CSV أصغر ومناسب لجداول البيانات؛ JSON أكثر تعبيراً ومناسب لواجهات API.
كيف أحول CSV إلى JSON في JavaScript؟
استخدم حزمة csv-parse في Node.js: اقرأ الملف بـ createReadStream، مرره عبر parse({ columns: true })، واجمع النتائج. للاستخدام في المتصفح، اقرأ الملف بـ FileReader، قسمه بالأسطر الجديدة، واربط الصفوف بكائنات باستخدام صف الرأس كمفاتيح.
كيف أحول CSV إلى JSON في Python؟
استخدم csv.DictReader من المكتبة القياسية لقراءة الصفوف كقواميس، ثم json.dump() لكتابتها كمصفوفة JSON. لمعالجة البيانات قبل التحويل، pandas.read_csv() متبوعة بـ df.to_json(orient='records') هي بديل من سطر واحد.
هل يمكن تحويل JSON المتداخل إلى CSV؟
نعم، لكنك تحتاج لاستراتيجية تسطيح. النهج الأكثر شيوعاً يستخدم التدوين بالنقطة: حقل مثل address.city يصبح رأس عمود. في Python، pandas.json_normalize() يتعامل مع هذا تلقائياً. المصفوفات تتطلب قرارات إضافية — الدمج كسلاسل نصية، التوسيع إلى أعمدة، أو التوسيع إلى صفوف.
لماذا يحتوي ملف CSV على أحرف مشوهة بعد التحويل؟
عدم تطابق الترميز. من المحتمل أن الملف حُفظ بترميز Windows-1252 أو GBK، لكن المحلل يفترض UTF-8. استخدم مكتبة اكتشاف مثل chardet (Python) لتحديد الترميز، ثم حدده صراحة عند القراءة. تحقق أيضاً من وجود علامة UTF-8 BOM التي تضيفها بعض الأدوات تلقائياً.
كيف أتعامل مع ملف CSV أكبر من 100 ميجابايت؟
استخدم البث بدلاً من تحميل الملف بالكامل في الذاكرة. في Node.js، مرره عبر csv-parse مع التدفقات. في Python، كرر باستخدام csv.DictReader مع مولد. فكر في إخراج تنسيق JSON Lines (.jsonl) بدلاً من مصفوفة JSON واحدة لتسهيل المعالجة اللاحقة.
كيف يمكنني التحقق من صحة JSON المحول؟
الصق المخرجات في منسق JSON عبر الإنترنت للتحقق من الصياغة والبنية والتداخل. للتحقق الآلي، استخدم JSON.parse() في JavaScript أو json.loads() في Python — كلاهما يرمي أخطاء واضحة عند المدخلات غير الصالحة. للتحقق من المخطط، حدد JSON Schema وتحقق برمجياً.
النقاط الرئيسية
- اختر الطريقة المناسبة لسياقك: أدوات المتصفح للتحويلات السريعة لمرة واحدة، الكود للأتمتة، سطر الأوامر للأنابيب.
- انتبه لمشكلات الترميز — حدد الترميز صراحة دائماً بدلاً من الاعتماد على القيم الافتراضية.
- احتفظ بالأنواع عمداً — الأصفار البادئة والأعداد الصحيحة الكبيرة وقيم null كلها تحتاج معالجة صريحة.
- ابث الملفات الكبيرة بدلاً من تحميلها في الذاكرة. فكر في تنسيق JSON Lines لمجموعات البيانات الكبيرة جداً.
- تحقق من مخرجاتك — مرر JSON المحول عبر منسق JSON لاكتشاف المشكلات الهيكلية قبل أن تصل إلى الإنتاج.