CSV to JSON JavaScript — Converter + Code Examples
استخدم محوّل CSV إلى JSON المجاني مباشرةً في متصفحك — لا حاجة للتثبيت.
جرّب محوّل CSV إلى JSON أونلاين ←معظم بيانات CSV التي أصادفها تصل كنص مسطح من رفع ملف، أو تصدير قاعدة بيانات، أو API لا يزال يصدّر CSV. لـ تحويل CSV إلى JSON في JavaScript، تحتاج شيئين تمنحهما اللغة مجاناً: تقسيم النص لتحليل الصفوف و JSON.stringify() لتسلسل النتيجة. لا حاجة لحزم npm للأساسيات — يغطي هذا الدليل المسار الكامل من أداة csvToJson() القابلة لإعادة الاستخدام عبر PapaParse وعمليات I/O في Node.js. للتحويلات السريعة بدون كود، يتولى محوّل CSV إلى JSON أونلاين الأمر فوراً. جميع الأمثلة تعمل على Node.js 18+ والمتصفحات الحديثة.
- ✓قسّم CSV بالسطر الجديد، واستخرج الترويسات من الصف الأول، وعيّن الصفوف المتبقية إلى كائنات، ثم JSON.stringify(array, null, 2) للإخراج المنسّق.
- ✓JSON.stringify() يُنتج نصاً؛ JSON.parse() يحوّله إلى مصفوفة JavaScript حية — اعرف أيهما لديك قبل التعامل معه.
- ✓نسخ Map لا تُسلسَل إلى JSON تلقائياً — استدعِ Object.fromEntries(map) أولاً.
- ✓لبيانات CSV تحتوي على حقول محاطة باقتباسات، أو فواصل داخل القيم، أو أسطر جديدة في الخلايا، استخدم PapaParse أو csv-parse بدلاً من التقسيم اليدوي.
- ✓تتولى مكتبة csvtojson (npm) تحويل الأنواع والبث المتدفق وحالات RFC 4180 الحدية في استدعاء واحد.
ما هو تحويل CSV إلى JSON؟
يحوّل تحويل CSV إلى JSON صيغة نص مسطحة محددة بالفواصل إلى مصفوفة منظمة من الكائنات حيث يصبح كل صف كائن JavaScript مفاتيحه ترويسات الأعمدة. لا يمتلك CSV أنواع بيانات — كل شيء نص. يضيف JSON البنية والتداخل والأنواع الصريحة (أرقام، قيم منطقية، null). هذا التحويل هو الخطوة الأولى في تقريباً كل مسار بيانات يبدأ بتصدير جدول بيانات، أو تفريغ نظام قديم، أو ملف رفعه المستخدم. تبقى البيانات الأساسية كما هي؛ تتغير الصيغة من أعمدة محددة بالموضع إلى خصائص مسماة.
[
{
"name": "محمد الأحمد",
"email": "m.ahmad@nexuslabs.io",
"role": "Engineering Lead",
"active": "true"
},
{
"name": "فاطمة العلي",
"email": "f.ali@nexuslabs.io",
"role": "Product Manager",
"active": "true"
}
]name,email,role,active محمد الأحمد,m.ahmad@nexuslabs.io,Engineering Lead,true فاطمة العلي,f.ali@nexuslabs.io,Product Manager,true
csvToJson() — بناء دالة تحويل قابلة لإعادة الاستخدام
يتكون مسار CSV-to-JSON في JavaScript من ثلاث خطوات: قسّم نص CSV بالسطر الجديد للحصول على الصفوف، استخرج الترويسات من الصف الأول بـ split(',')، ثم عيّن كل صف متبقٍ إلى كائن JavaScript عادي مفاتيحه من الترويسات وقيمه من مواضع الأعمدة المقابلة. الاستدعاء الأخير لـ JSON.stringify() يحوّل تلك المصفوفة من الكائنات إلى نص JSON. إليك نسخة عمل بسيطة:
function csvToJson(csv) {
const lines = csv.trim().split('\n')
const headers = lines[0].split(',').map(h => h.trim())
const rows = lines.slice(1)
.filter(line => line.trim() !== '')
.map(line => {
const values = line.split(',')
return Object.fromEntries(
headers.map((header, i) => [header, values[i]?.trim()])
)
})
return JSON.stringify(rows, null, 2)
}
const csv = `server,port,region,status
api-gateway,8080,us-east-1,healthy
auth-service,8443,eu-west-1,degraded
payments-api,9090,ap-south-1,healthy`
console.log(csvToJson(csv))
// [
// { "server": "api-gateway", "port": "8080", "region": "us-east-1", "status": "healthy" },
// { "server": "auth-service", "port": "8443", "region": "eu-west-1", "status": "degraded" },
// { "server": "payments-api", "port": "9090", "region": "ap-south-1", "status": "healthy" }
// ]تتعامل هذه الدالة مع الأساسيات: الأسطر الجديدة اللاحقة، والأسطر الفارغة، والمسافات حول القيم. كل حقل CSV يمر كنص. لاحظ أن port هو "8080" (نص)، وليس 8080 (رقم). إذا احتجت أنواعاً صحيحة في مخرجات JSON، عليك تحويلها بنفسك. إليك نسخة موسّعة مع اكتشاف الأنواع:
function coerceValue(val) {
if (val === undefined || val === '') return null
if (val === 'true') return true
if (val === 'false') return false
if (val === 'null') return null
const num = Number(val)
if (!isNaN(num) && val.trim() !== '') return num
return val
}
function csvToJson(csv, { coerce = false } = {}) {
const lines = csv.trim().split('\n')
const headers = lines[0].split(',').map(h => h.trim())
const rows = lines.slice(1)
.filter(line => line.trim() !== '')
.map(line => {
const values = line.split(',')
return Object.fromEntries(
headers.map((header, i) => [
header,
coerce ? coerceValue(values[i]?.trim()) : values[i]?.trim()
])
)
})
return JSON.stringify(rows, null, 2)
}
const csv = `endpoint,port,max_connections,debug
/api/v2/orders,8443,500,true
/api/v2/health,8080,100,false`
console.log(csvToJson(csv, { coerce: true }))
// [
// { "endpoint": "/api/v2/orders", "port": 8443, "max_connections": 500, "debug": true },
// { "endpoint": "/api/v2/health", "port": 8080, "max_connections": 100, "debug": false }
// ]خيار coerce اختياري لأن اكتشاف الأنواع التلقائي قد يأتي بنتائج عكسية — حقل مثل الرمز البريدي ("07302") يفقد صفره الأمامي عند تحويله إلى رقم. أبقِ تحويل الأنواع معطلاً بشكل افتراضي وشغّله فقط عندما تتحكم في المخطط. ملاحظة سريعة: JSON.stringify() يقبل معاملاً ثالثاً space للمسافة البادئة. مرّر 2 لمسافتين، 4 لأربع، أو "\t" للجدولة. اتركه كلياً للحصول على إخراج مضغوط في سطر واحد — مفيد عند إرسال نص JSON كجسم طلب API حيث المسافات تهدر عرض النطاق الترددي.
JSON.parse() مباشرة على نص CSV يرمي SyntaxError. يجب أولاً تحويل CSV إلى كائنات JavaScript بدالة csvToJson() التي تستدعي داخلياً JSON.stringify() لإنتاج نص JSON الفعلي.التعامل مع Map والتواريخ والكائنات المخصصة من بيانات CSV
لا تنتهي كل عمليات تحويل CSV بمصفوفة مسطحة من الكائنات العادية. أحياناً تحتاج إلى بناء Map من أزواج ترويسة-قيمة، أو تحليل نصوص التواريخ إلى كائنات Date، أو إرفاق خصائص محسوبة قبل التسلسل. في JavaScript ثمة خاصية تُوقع الناس في الفخ: نسخ Map لا تُسلسَل بـ JSON.stringify(). ستحصل على كائن فارغ. الحل هو Object.fromEntries() لتحويل Map إلى كائن عادي قبل التسلسل.
سبب تسلسل Map إلى {} هو أن JSON.stringify() يتكرر على الخصائص المعدودة المملوكة للكائن. تخزّن Map مدخلاتها في فتحة داخلية، لا كخصائص معدودة على الكائن نفسه، لذا يرى المسلسِّل كائناً بلا مفاتيح. يفتقر نموذج Map أيضاً إلى طريقة toJSON()، وهي الخطّاف الذي يستدعيه JSON.stringify() أولاً على أي قيمة قبل تقرير كيفية تسلسلها. إذا امتلكت قيمة ما toJSON()، فالقيمة المُعادة من الطريقة هي ما يُسلسَل — لا الكائن نفسه. لهذا تُسلسَل كائنات Date بشكل صحيح: يُعيد Date.prototype.toJSON نص ISO 8601، لذا يُنتج JSON.stringify(new Date()) طابعاً زمنياً بين اقتباسات لا كائناً فارغاً. فهم هذا الخطّاف يتيح لك تعريف السلوك ذاته على صفوفك الخاصة — كما هو موضح في مثال EmployeeRecord أدناه — للتحكم بالضبط في حقول CSV التي تظهر في مخرجات JSON النهائية.
تحويل Map إلى JSON
// Build a Map from CSV header→value pairs
const headers = ['server', 'port', 'region']
const values = ['payments-api', '9090', 'ap-south-1']
const rowMap = new Map(headers.map((h, i) => [h, values[i]]))
// Map does NOT serialize directly
console.log(JSON.stringify(rowMap))
// "{}" — empty object, data lost!
// Convert to plain object first
const rowObj = Object.fromEntries(rowMap)
console.log(JSON.stringify(rowObj, null, 2))
// {
// "server": "payments-api",
// "port": "9090",
// "region": "ap-south-1"
// }نصوص التواريخ وtoJSON()
تصل حقول تواريخ CSV كنصوص. إذا حلّلتها إلى كائنات Date أثناء المعالجة، تُسلسَل هذه الكائنات بشكل صحيح لأن Date تمتلك طريقة toJSON() مدمجة تُعيد نص ISO 8601. يمكنك أيضاً تعريف toJSON() المخصصة على صفوفك الخاصة للتحكم في أعمدة CSV التي تظهر في المخرجات المسلسلة — مثلاً، حذف حقول التتبع الداخلية مثل _rowIndex.
class EmployeeRecord {
constructor(csvRow) {
this._rowIndex = csvRow._rowIndex // internal, not for JSON
this.employeeId = csvRow.employee_id
this.name = csvRow.name
this.hiredAt = new Date(csvRow.hired_date)
this.salary = Number(csvRow.salary)
}
toJSON() {
// Only expose fields we want in the JSON output
return {
employee_id: this.employeeId,
name: this.name,
hired_at: this.hiredAt, // Date.toJSON() → ISO string automatically
salary: this.salary,
}
}
}
const csvRow = {
_rowIndex: 42,
employee_id: 'EMP-2847',
name: 'محمد الأحمد',
hired_date: '2024-03-15',
salary: '128000',
}
const record = new EmployeeRecord(csvRow)
console.log(JSON.stringify(record, null, 2))
// {
// "employee_id": "EMP-2847",
// "name": "محمد الأحمد",
// "hired_at": "2024-03-15T00:00:00.000Z",
// "salary": 128000
// }
// Note: _rowIndex is excluded, salary is a number, date is ISO formatReviver لإعادة بناء كائنات التاريخ
بعد كتابة JSON المشتق من CSV إلى ملف أو إرساله عبر الشبكة، JSON.parse() يُعيد لك كائنات عادية — تعود كائنات Date مرة أخرى كنصوص. استخدم دالة reviver لتحويل نصوص ISO 8601 إلى كائنات Date أثناء التحليل:
const jsonString = '{"employee_id":"EMP-2847","name":"محمد الأحمد","hired_at":"2024-03-15T00:00:00.000Z","salary":128000}'
const isoDatePattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/
const parsed = JSON.parse(jsonString, (key, value) => {
if (typeof value === 'string' && isoDatePattern.test(value)) {
return new Date(value)
}
return value
})
console.log(parsed.hired_at instanceof Date) // true
console.log(parsed.hired_at.getFullYear()) // 2024JSON.stringify() القيمة undefined (لا نصاً) للقيم التي تحتوي على دوال، أو Symbols، أو خصائص undefined. إذا التقطت كائناتك المشتقة من CSV عن طريق الخطأ قيمة undefined من عمود مفقود، تختفي تلك الخاصية بصمت من مخرجات JSON. استخدم دائماً null بدلاً من ذلك للقيم المفقودة.مرجع معاملات JSON.stringify()
توقيع الدالة هو JSON.stringify(value, replacer?, space?). معاملا replacer و space كلاهما اختياريان — مرّر null للـ replacer عندما تحتاج فقط المسافة البادئة.
معاملات JSON.parse():
JSON.parse() — استهلاك مخرجات JSON
بمجرد حصولك على نص JSON من csvToJson()، الخطوة التالية عادةً تحليله إلى مصفوفة JavaScript حية للتصفية أو التعيين أو التغذية إلى API. الفرق بين نص JSON (typeof === "string") وكائن JavaScript مهم. لا يمكنك استدعاء .filter() أو الوصول إلى [0].name على نص — تحتاج JSON.parse() أولاً. تعمل هذه الرحلة الكاملة (stringify ثم parse) أيضاً كأسلوب تحقق: إذا أنتج تحويل CSV شيئاً ليس JSON صحيحاً، سيرمي parse استثناءً. معامل reviver الاختياري يتيح تحويل كل زوج مفتاح-قيمة أثناء التحليل — مفيد لاستعادة كائنات Date من نصوص ISO أو إعادة تسمية المفاتيح دون مرور منفصل.
const csv = `endpoint,method,avg_latency_ms,error_rate
/api/v2/orders,POST,342,0.02
/api/v2/health,GET,12,0.00
/api/v2/payments,POST,890,0.15
/api/v2/users,GET,45,0.01`
// Step 1: convert CSV to JSON string
const jsonString = csvToJson(csv, { coerce: true })
// Step 2: parse back to JavaScript array
const endpoints = JSON.parse(jsonString)
// Verify it is an array
console.log(Array.isArray(endpoints)) // true
// Filter high-latency endpoints
const slow = endpoints.filter(ep => ep.avg_latency_ms > 200)
console.log(slow.map(ep => ep.endpoint))
// ["/api/v2/orders", "/api/v2/payments"]
// Destructure the first row
const [first, ...rest] = endpoints
console.log(first.endpoint) // "/api/v2/orders"
console.log(rest.length) // 3غلاف آمن لـ JSON.parse() مفيد عند التحقق من مخرجات التحويل قبل المعالجة اللاحقة. إذا أنتج تحويل CSV JSON مشوّهاً لأي سبب (إدخال مقطوع، أخطاء ترميز)، يلتقط ذلك دون تعطل:
function safeParse(jsonString) {
try {
return { data: JSON.parse(jsonString), error: null }
} catch (err) {
return { data: null, error: err.message }
}
}
// Valid output
const result = safeParse(csvToJson(csv))
if (result.error) {
console.error('CSV conversion produced invalid JSON:', result.error)
} else {
console.log(`Parsed ${result.data.length} rows`)
}
// Accidentally passing raw CSV to JSON.parse — this fails
const bad = safeParse('name,email\nمحمد,m.ahmad@nexuslabs.io')
console.log(bad.error) // "Unexpected token 'a', "name,email"... is not valid JSON"Reviver لإعادة تسمية المفاتيح والتحقق
تستقبل دالة reviver كل زوج مفتاح-قيمة أثناء التحليل، من أكثر الخصائص تداخلاً إلى الخارج. إعادة undefined لمفتاح يحذفه من النتيجة كلياً؛ إعادة قيمة مختلفة يستبدله. الـ reviver مفيد لإعادة تسمية الترويسات (camelCase إلى snake_case)، وحذف الحقول الداخلية، أو التحقق من وجود الأعمدة المطلوبة. يُستدعى مع القيمة الجذر أخيراً (مفتاح نص فارغ)، وهو المكان الذي ترمي فيه إذا لم تكن النتيجة مصفوفة.
const jsonString = csvToJson(`employeeId,firstName,hiredDate
EMP-2847,محمد,2024-03-15
EMP-3012,فاطمة,2023-11-01`, { coerce: false })
const camelToSnake = str => str.replace(/[A-Z]/g, c => '_' + c.toLowerCase())
const employees = JSON.parse(jsonString, function(key, value) {
// Root value — validate shape
if (key === '') {
if (!Array.isArray(value)) throw new Error('Expected JSON array from CSV')
return value
}
// Rename camelCase header keys to snake_case
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
return Object.fromEntries(
Object.entries(value).map(([k, v]) => [camelToSnake(k), v])
)
}
return value
})
console.log(employees[0])
// { employee_id: 'EMP-2847', first_name: 'محمد', hired_date: '2024-03-15' }تحويل CSV من ملف واستجابة API
المصدران الفعليان لبيانات CSV في الإنتاج: الملفات على القرص واستجابات HTTP. يحتاج كلا السيناريوهين معالجة أخطاء لأن الإدخال خارجي وغير متحكم به.
قراءة ملف CSV، التحويل، كتابة JSON
import { readFileSync, writeFileSync } from 'node:fs'
function csvToJsonFromFile(inputPath, outputPath) {
let csvText
try {
csvText = readFileSync(inputPath, 'utf8')
} catch (err) {
throw new Error(`Failed to read ${inputPath}: ${err.message}`)
}
const lines = csvText.trim().split('\n')
if (lines.length < 2) {
throw new Error(`${inputPath} has no data rows (only ${lines.length} line)`)
}
const headers = lines[0].split(',').map(h => h.trim())
const rows = lines.slice(1)
.filter(line => line.trim() !== '')
.map(line => {
const values = line.split(',')
return Object.fromEntries(headers.map((h, i) => [h, values[i]?.trim()]))
})
const jsonOutput = JSON.stringify(rows, null, 2)
writeFileSync(outputPath, jsonOutput, 'utf8')
console.log(`Converted ${rows.length} rows → ${outputPath}`)
return rows
}
// Usage
const data = csvToJsonFromFile('inventory.csv', 'inventory.json')
console.log(data[0])
// { sku: "WDG-2847", warehouse: "us-east-1", quantity: "150", ... }جلب CSV من نقطة نهاية API
async function fetchCsvAsJson(url) {
const response = await fetch(url)
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
const contentType = response.headers.get('content-type') || ''
if (!contentType.includes('text/csv') && !contentType.includes('text/plain')) {
console.warn(`Unexpected content-type: ${contentType}`)
}
const csvText = await response.text()
const lines = csvText.trim().split('\n')
const headers = lines[0].split(',').map(h => h.trim())
const rows = lines.slice(1)
.filter(line => line.trim() !== '')
.map(line => {
const values = line.split(',')
return Object.fromEntries(headers.map((h, i) => [h, values[i]?.trim()]))
})
return rows
}
// Example: fetch exchange rate CSV from a data provider
try {
const rates = await fetchCsvAsJson('https://data.ecb.internal/rates/daily.csv')
console.log(JSON.stringify(rates.slice(0, 3), null, 2))
// Send as JSON to downstream service
await fetch('https://api.internal/v2/rates', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ data: rates }),
})
} catch (err) {
console.error('Rate sync failed:', err.message)
}JSON.stringify() تحديد أعمدة معينة من CSV بالاسم لتضمينها فقط. مرّر مصفوفة من أسماء الترويسات: JSON.stringify(rows, ['name', 'email', 'department']). الخصائص الغائبة عن المصفوفة تُستبعد بصمت من المخرجات.تحويل CSV إلى JSON من سطر الأوامر
يمكن لـ Node.js تشغيل سكريبتات مضمّنة، وثمة أدوات CLI مخصصة تتعامل مع تحويل CSV-to-JSON دون كتابة سكريبت.
# Pipe CSV to a Node.js inline script
cat servers.csv | node -e "
const lines = require('fs').readFileSync('/dev/stdin','utf8').trim().split('\n');
const h = lines[0].split(',');
const rows = lines.slice(1).map(l => Object.fromEntries(h.map((k,i) => [k.trim(), l.split(',')[i]?.trim()])));
console.log(JSON.stringify(rows, null, 2));
"# Miller is a Swiss Army knife for structured data # Install: brew install miller (macOS) or apt install miller (Debian/Ubuntu) mlr --icsv --ojson cat inventory.csv # Filter rows during conversion mlr --icsv --ojson filter '$quantity > 100' inventory.csv # Select specific columns mlr --icsv --ojson cut -f sku,warehouse,quantity inventory.csv
# Install globally npm install -g csvtojson # Convert file csvtojson servers.csv > servers.json # Pipe from stdin cat exports/q1-metrics.csv | csvtojson > q1-metrics.json
للملفات الكبيرة، Miller عادةً الخيار الأفضل مقارنةً بـ csvtojson. Miller مُنفَّذ بلغة C ويعالج CSV كتدفق دون تحميل الملف كاملاً في الذاكرة، مما يعني معالجة تصديرات بعدة غيغابايت باستهلاك ذاكرة ثابت. يدعم أيضاً العمليات على مستوى الحقول في مكانها — إعادة تسمية الأعمدة، وتحويل أنواع القيم، وتصفية الصفوف — قبل أن تصبح البيانات JSON، مما يجنبك مسار تحليل-ثم-تحويل من خطوتين. csvtojson من ناحية أخرى يعمل في Node.js وأكثر ملاءمة عندما تكون بقية أدواتك JavaScript: يمكنك توجيه مخرجاته مباشرة إلى تدفقات Node، أو استيراده كمكتبة، أو استخدام واجهة colParser لتحويل أنواع لكل عمود في الكود. فضّل Miller للإنتاجية الخام وخطوط أوامر الصدفة؛ وفضّل csvtojson عندما تحتاج تكاملاً محكماً مع تطبيق Node.js.
jq بيانات CSV بشكل أصلي. إذا احتجت jq في المسار، حوّل إلى JSON أولاً بـ csvtojson أو mlr، ثم وجّه مخرجات JSON إلى jq للتصفية والتحويل.بديل عالي الأداء — PapaParse
أسلوب split(',') اليدوي يفشل مع ملفات CSV الواقعية. الحقول المحاطة باقتباسات والتي تحتوي على فواصل، والأسطر الجديدة المضمّنة، وعلامات الاقتباس المزدوجة المهرّبة — كل هذه تُفسد مقسّماً ساذجاً. PapaParse هي المكتبة التي أتوجه إليها عندما تأتي CSV من مصدر غير معروف. تتعامل مع كل حالات RFC 4180 الحدية، وتكتشف المحددات تلقائياً، وتعمل في Node.js والمتصفحات معاً.
npm install papaparse
import Papa from 'papaparse'
const csv = `product,description,price,in_stock
"Widget, Large","A premium widget with ""extra"" features",29.99,true
Bolt Assembly,Standard M8 bolt kit,4.50,true
"Gasket Set","Includes gasket, seal, and O-ring",12.75,false`
const { data, errors, meta } = Papa.parse(csv, {
header: true,
dynamicTyping: true, // auto-converts numbers and booleans
skipEmptyLines: true,
transformHeader: h => h.trim().toLowerCase().replace(/\s+/g, '_'),
})
if (errors.length > 0) {
console.error('Parse errors:', errors)
}
console.log(JSON.stringify(data, null, 2))
// [
// {
// "product": "Widget, Large",
// "description": "A premium widget with \"extra\" features",
// "price": 29.99,
// "in_stock": true
// },
// ...
// ]
console.log(`Parsed ${data.length} rows, delimiter: "${meta.delimiter}"`)خيار dynamicTyping في PapaParse يجري تحويل الأنواع تلقائياً — تصبح الأرقام أرقاماً، و"true"/"false" تصبح قيماً منطقية. استدعاء transformHeader يوحّد أسماء الأعمدة إلى snake_case، مما يجنبك التعامل مع ترويسات غير متسقة من تصديرات CSV مختلفة. للتحويلات السريعة بدون كتابة كود تحليل، محوّل CSV إلى JSON يتولى كل ذلك في المتصفح.
إخراج الطرفية مع تمييز بناء الجملة
إخراج مصفوفة JSON كبيرة إلى الطرفية يصعب قراءتها بدون تنسيق. إضافة تمييز بناء الجملة للإخراج تجعله مقروءاً أثناء التصحيح والتطوير. حزمة cli-highlight تلوّن إخراج JSON في طرفيات Node.js.
npm install cli-highlight
import { highlight } from 'cli-highlight'
// After converting CSV to JSON array
const jsonOutput = JSON.stringify(rows, null, 2)
// Print with syntax highlighting
console.log(highlight(jsonOutput, { language: 'json' }))
// Keys, strings, numbers, and booleans each get distinct colorsالإخراج الملوّن يستحق استخدامه عند فحص نتيجة تحويل كبيرة بشكل تفاعلي. مفاتيح JSON وقيم النصوص والأرقام والقيم المنطقية كل منها يحصل على ألوان ANSI مميزة، مما يسهّل رصد حقل خاطئ النوع — مثلاً، رقم منفذ يجب أن يكون 8080 لكنه مميَّز كنص لأن تحويل الأنواع كان معطلاً. هذا مفيد بشكل خاص عند تصحيح ملفات CSV مُصدَّرة من أدوات جداول البيانات حيث أنواع الأعمدة غير متسقة عبر الصفوف. بدون ألوان، مسح 50 صفاً من JSON بحثاً عن حقل خاطئ النوع يعني قراءة كل قيمة على حدة. مع الألوان، الرقم المميَّز كنص يلفت النظر فوراً.
العمل مع ملفات CSV الكبيرة
تحميل ملف CSV بحجم 500 ميغابايت كنص بـ readFileSync() سيستهلك الذاكرة وقد يُعطل العملية. للملفات الكبيرة، دفِّق CSV سطراً بسطر وأصدر كائنات JSON فور وصولها. حزمة csv-parse (جزء من منظومة csv على npm) توفر محللاً بث متدفق يعمل مع تدفقات Node.js.
بث CSV إلى NDJSON بواسطة csv-parse
NDJSON (JSON محدود بالأسطر الجديدة) صيغة يكون فيها كل سطر من ملف الإخراج كائن JSON مكتفٍ بذاته. خلافاً لمصفوفة JSON كبيرة واحدة — التي تتطلب وجود الملف كاملاً في الذاكرة قبل البدء في قراءته — يمكن معالجة ملفات NDJSON سطراً بسطر. هذا يجعل NDJSON مثالياً لمجموعات البيانات الكبيرة التي ستستهلكها معالجات السجلات، أو خطوط التدفق، أو قواعد البيانات بواجهات الاستيراد الجماعي. تُصدر حزمة csv-parse كائن JavaScript واحداً لكل صف CSV في وضع الكائن، فيمكنك توجيهه مباشرة إلى تدفق تحويل يضيف \n بعد كل JSON.stringify(row).
import { createReadStream, createWriteStream } from 'node:fs'
import { parse } from 'csv-parse'
import { Transform } from 'node:stream'
import { pipeline } from 'node:stream/promises'
// Transform each CSV row object to a JSON line
const toNdjson = new Transform({
objectMode: true,
transform(record, encoding, callback) {
callback(null, JSON.stringify(record) + '\n')
},
})
await pipeline(
createReadStream('telemetry-2026-03.csv'),
parse({
columns: true, // use first row as headers
skip_empty_lines: true,
trim: true,
cast: true, // auto-convert numbers and booleans
}),
toNdjson,
createWriteStream('telemetry-2026-03.ndjson')
)
console.log('Streaming conversion complete')
// Each line in the output file is one JSON object:
// {"timestamp":"2026-03-15T08:22:00Z","service":"gateway","latency_ms":42,"status":200}
// {"timestamp":"2026-03-15T08:22:01Z","service":"auth","latency_ms":156,"status":401}
// ...بث PapaParse للمتصفح وNode.js
يستخدم وضع البث في PapaParse استدعاءً راجعاً step يُطلَق مرة لكل صف بدلاً من جمع كل الصفوف في الذاكرة. تمرّر له ReadStream من Node.js (في Node.js) أو كائن File (في المتصفح) ويتولى PapaParse التقسيم إلى أجزاء داخلياً. لا تربيط خط تدفق — فقط استدعاء راجع. استخدمه عندما تحتاج امتثالاً لـ RFC 4180 دون استقدام csv-parse.
import Papa from 'papaparse'
import { createReadStream } from 'node:fs'
let rowCount = 0
const errors = []
const fileStream = createReadStream('warehouse-inventory.csv')
Papa.parse(fileStream, {
header: true,
dynamicTyping: true,
step(result) {
// Process one row at a time — constant memory
rowCount++
if (result.data.quantity === 0) {
errors.push(`Row ${rowCount}: ${result.data.sku} is out of stock`)
}
},
complete() {
console.log(`Processed ${rowCount} rows`)
if (errors.length > 0) {
console.log(`Issues found: ${errors.length}`)
errors.forEach(e => console.log(` ${e}`))
}
},
error(err) {
console.error('Parse failed:', err.message)
},
})الأخطاء الشائعة
المشكلة: CSV ليس JSON. تمرير نص CSV خام إلى JSON.parse() يرمي SyntaxError لأن الفواصل والأسطر الجديدة ليست بناء جملة JSON صحيحاً.
الحل: حلّل CSV إلى كائنات JavaScript أولاً باستخدام split() أو مكتبة، ثم استخدم JSON.stringify() لإنتاج JSON. استدعِ JSON.parse() فقط على نصوص هي بالفعل JSON صحيح.
const csv = 'name,email\nمحمد الأحمد,m.ahmad@nexuslabs.io'
const lines = csv.trim().split('\n')
const headers = lines[0].split(',')
const rows = lines.slice(1).map(line =>
Object.fromEntries(headers.map((h, i) => [h, line.split(',')[i]]))
)
const json = JSON.stringify(rows, null, 2) // valid JSON stringconst csv = 'name,email\nمحمد الأحمد,m.ahmad@nexuslabs.io' const data = JSON.parse(csv) // SyntaxError: Unexpected token 'a'
المشكلة: استدعاء toString() على كائن JavaScript يُعيد النص غير المفيد '[object Object]' بدلاً من البيانات الفعلية. هذا يُدمّر مخرجات CSV-to-JSON بصمت.
الحل: استخدم دائماً JSON.stringify() لتحويل كائنات JavaScript إلى نصوص JSON. toString() موجود للتحويل من النوع الأولي إلى نص، لا للتسلسل.
const row = { server: 'api-gateway', port: 8080 }
const output = JSON.stringify(row, null, 2)
// '{"server":"api-gateway","port":8080}'const row = { server: 'api-gateway', port: 8080 }
const output = row.toString()
// "[object Object]" — data is goneالمشكلة: يفشل split(",") البسيط عندما تحتوي قيم CSV على فواصل داخل حقول محاطة باقتباسات: "Widget, Large" تصبح قيمتين منفصلتين بدلاً من واحدة.
الحل: استخدم PapaParse أو csv-parse لأي بيانات CSV لا تتحكم فيها بالكامل. إذا كان لا بد من التحليل اليدوي، نفّذ محللاً بآلية حالة يتتبع ما إذا كان الموضع الحالي داخل حقل محاط باقتباسات.
import Papa from 'papaparse'
const { data } = Papa.parse('"Widget, Large","Premium quality",29.99')
// data[0] = ["Widget, Large", "Premium quality", "29.99"]
// 3 values, correctly parsedconst line = '"Widget, Large","Premium quality",29.99'
const values = line.split(',')
// ["\"Widget", " Large\"", "\"Premium quality\"", "29.99"]
// 4 values instead of 3 — first field split incorrectlyالمشكلة: بدون تحويل الأنواع، يبقى port: "8080" كنص في JSON بدلاً من رقم. الأنظمة اللاحقة التي تتوقع أنواعاً رقمية ترفض البيانات أو تتعامل معها بشكل خاطئ.
الحل: طبّق تحويل أنواع صريحاً أثناء خطوة التعيين، أو استخدم PapaParse مع dynamicTyping: true. كن متعمداً دائماً بشأن الحقول التي يجب أن تكون رقمية.
const row = {
port: Number('8443'), // 8443
debug: 'true' === 'true', // true
workers: Number('4'), // 4
}
JSON.stringify(row)
// {"port":8443,"debug":true,"workers":4} — correct typesconst row = { port: '8443', debug: 'true', workers: '4' }
JSON.stringify(row)
// {"port":"8443","debug":"true","workers":"4"} — all stringsالتحليل اليدوي مقابل المكتبات — مقارنة سريعة
للسكريبتات السريعة حيث تتحكم في صيغة CSV وتعلم عدم وجود حقول محاطة باقتباسات، يعمل أسلوب split() + JSON.stringify() المدمج ولا يتطلب أي تبعيات. للأنظمة الإنتاجية التي تعالج ملفات CSV مرفوعة من المستخدمين، استخدم PapaParse في المتصفح أو csv-parse في Node.js — كلاهما يتعامل مع RFC 4180 بشكل صحيح ويدعم البث. حزمة csvtojson هي الوحيدة التي تُخرج JSON مباشرة، متولّيةً التحليل والتسلسل في استدعاء واحد. عندما تحتاج أسرع مسار من اللصق إلى النتيجة، محوّل CSV إلى JSON يتولى كل شيء في المتصفح بدون أي إعداد.
الأسئلة الشائعة
كيف أحوّل CSV إلى JSON في JavaScript بدون مكتبة؟
قسّم نص CSV باستخدام السطر الجديد للحصول على الصفوف، واستخرج الترويسات من الصف الأول بواسطة split(",")، ثم حوّل الصفوف المتبقية إلى كائنات مفاتيحها هذه الترويسات. أنهِ العملية بـ JSON.stringify(array, null, 2) للحصول على إخراج JSON منسّق. يعمل هذا الأسلوب بشكل جيد لملفات CSV البسيطة التي تتحكم في صيغتها وتضمن عدم وجود حقول بين علامات اقتباس أو فواصل مضمّنة. أما البيانات المُصدَّرة من جداول البيانات أو الأنظمة الخارجية، فقد تتضمن حقولاً بين اقتباسات وقيماً متعددة الأسطر تُفسد المقسّم البسيط — استخدم PapaParse أو csv-parse في تلك الحالات. للملفات الصغيرة جداً المعالَجة في المتصفح، هذا الأسلوب بلا تبعيات مثالي.
const csv = `name,email,department
محمد الأحمد,m.ahmad@nexuslabs.io,Engineering
فاطمة العلي,f.ali@nexuslabs.io,Product`
const lines = csv.trim().split('\n')
const headers = lines[0].split(',')
const rows = lines.slice(1).map(line => {
const values = line.split(',')
return Object.fromEntries(headers.map((h, i) => [h.trim(), values[i]?.trim()]))
})
console.log(JSON.stringify(rows, null, 2))
// [
// { "name": "محمد الأحمد", "email": "m.ahmad@nexuslabs.io", "department": "Engineering" },
// { "name": "فاطمة العلي", "email": "f.ali@nexuslabs.io", "department": "Product" }
// ]ما الفرق بين JSON.stringify() وtoString() للكائنات؟
يُعيد toString() على كائن عادي النص غير المفيد "[object Object]" — لا يوضح شيئاً عن البيانات الفعلية. أما JSON.stringify() فيُنتج نصاً JSON صحيحاً يحتوي على جميع المفاتيح والقيم بتسلسل صحيح، بما في ذلك الكائنات والمصفوفات المتداخلة. استخدم دائماً JSON.stringify() عند الحاجة إلى تحويل كائن JavaScript (مثل صف مستخرج من CSV) إلى نص JSON. لعكس العملية — إعادة بناء كائنات JavaScript الحية من نص JSON — استخدم JSON.parse()، وهو المعكوس التام لـ JSON.stringify(). تشكّل هاتان الدالتان رحلة كاملة ذهاباً وإياباً: stringify للحفظ أو الإرسال، وparse للاستهلاك.
const row = { name: 'محمد الأحمد', role: 'Engineer' }
console.log(row.toString()) // "[object Object]"
console.log(JSON.stringify(row)) // '{"name":"محمد الأحمد","role":"Engineer"}'كيف أتعامل مع حقول CSV تحتوي على فواصل أو علامات اقتباس؟
تنص RFC 4180 على أن الحقول التي تحتوي على فواصل أو أسطر جديدة أو علامات اقتباس مزدوجة يجب وضعها بين علامات اقتباس مزدوجة، مع تهريب علامات الاقتباس المضمّنة بمضاعفتها ("" داخل حقل محاط باقتباسات تمثل اقتباساً حرفياً واحداً). يُفشل split(",") البسيط هذه الملفات — إذ لم تعد حدود الحقل فاصلة عادية. استخدم PapaParse أو csv-parse للبيانات الإنتاجية، أو اكتب محللاً بآلية حالة يتتبع ما إذا كنت داخل حقل محاط باقتباسات. كتابة آلة حالة صحيحة من الصفر أمر معقد بشكل مفاجئ: يجب التعامل مع الاقتباسات في بداية الحقل، والاقتباسات المهرّبة في منتصف الحقل، والأسطر الجديدة داخل الحقول المحاطة باقتباسات، واتفاقيات نهاية الأسطر المختلفة (CRLF مقابل LF). لأي شيء يتجاوز بيانات CSV بسيطة، استخدم مكتبة مُختبرة جيداً.
// PapaParse handles RFC 4180 correctly
import Papa from 'papaparse'
const csv = `product,description,price
"Widget, Large","A big ""widget""",29.99
Bolt,Standard bolt,1.50`
const { data } = Papa.parse(csv, { header: true })
console.log(JSON.stringify(data, null, 2))
// description field correctly contains: A big "widget"هل يمكنني تحويل CSV إلى JSON مباشرة في المتصفح؟
نعم. كلٌّ من JSON.stringify() وJSON.parse() مدمجان في كل محرك متصفح. لخطوة تحليل CSV، يمكنك التقسيم بالسطر الجديد والفاصلة للملفات البسيطة، أو تضمين PapaParse عبر CDN (لا تبعيات لـ Node.js لديه). يجري التحويل بالكامل على جانب العميل دون الحاجة إلى خادم، وهو مفيد لحماية الخصوصية — إذ لا تغادر بيانات CSV الخام جهاز المستخدم. عند رفع المستخدم ملف CSV عبر عنصر <input type="file">، تُعيد طريقة file.text() من File API وعداً (Promise) يتحل إلى محتوى الملف كنص، يمكن تمريره لدالة التحويل. هذا يجعل تحويل CSV إلى JSON بالكامل في المتصفح عملياً للوحات المعلومات وأدوات المطورين وأي تطبيق يحتاج معالجة البيانات المرفوعة بدون خادم خلفي.
// Browser: user uploads a CSV file
const fileInput = document.querySelector('input[type="file"]')
fileInput.addEventListener('change', async (event) => {
const file = event.target.files[0]
const text = await file.text()
const lines = text.trim().split('\n')
const headers = lines[0].split(',')
const rows = lines.slice(1).map(line =>
Object.fromEntries(headers.map((h, i) => [h.trim(), line.split(',')[i]?.trim()]))
)
console.log(JSON.stringify(rows, null, 2))
})كيف أحلّل القيم الرقمية والمنطقية من CSV بدلاً من الإبقاء على كل شيء كنصوص؟
لا يوجد في CSV نظام أنواع — كل حقل نص. يجب إجراء تحويل الأنواع أثناء خطوة التعيين. تحقق من الأنماط الرقمية باستخدام Number() أو parseFloat()، وحوّل "true"/"false" إلى قيم منطقية، وتعامل مع النصوص الفارغة بوصفها null. احذر من الحقول التي تبدو أرقاماً لكن يجب الإبقاء عليها كنصوص: الرموز البريدية مثل "07302" تفقد صفرها الأمامي عند التحويل بـ Number()، وأرقام الهواتف أو رموز المنتجات ذات الطابع الرقمي هشة بالمثل. تُجري مكتبة csvtojson تحويل الأنواع تلقائياً عبر خيار colParser الذي يتيح تحديد دوال تحويل لكل عمود وتجاوز الاكتشاف التلقائي للأعمدة الإشكالية. خيار dynamicTyping في PapaParse يطبق التحويل ذاته على جميع الأعمدة.
function coerceValue(val) {
if (val === '') return null
if (val === 'true') return true
if (val === 'false') return false
const num = Number(val)
if (!isNaN(num) && val.trim() !== '') return num
return val
}
// Apply during CSV-to-object mapping
const row = { port: coerceValue('8443'), debug: coerceValue('true'), host: coerceValue('api.internal') }
// { port: 8443, debug: true, host: "api.internal" }كيف أكتب مخرجات CSV-to-JSON إلى ملف في Node.js؟
استخدم fs.writeFileSync() مع مخرجات JSON.stringify(). المعامل الثالث لـ JSON.stringify يتحكم في المسافة البادئة — مرّر 2 لمسافتين أو "\t" للجدولة. لقراءة الملف مرة أخرى، استخدم JSON.parse(fs.readFileSync(path, "utf8"))، الذي يُعيد بناء مصفوفة الكائنات الحية. إذا كنت تكتب الملف في سياق غير متزامن (داخل دالة async أو في المستوى الأعلى لوحدة ES)، فضّل fs.promises.writeFile() لتجنب إعاقة حلقة الأحداث أثناء كتابة الملف. للمخرجات الكبيرة من JSON التي تتجاوز بضعة ميغابايت، فكّر في توجيه تدفق JSON إلى WriteStream بدلاً من بناء النص كاملاً في الذاكرة قبل الكتابة.
import { writeFileSync, readFileSync } from 'node:fs'
// Write
const jsonOutput = JSON.stringify(rows, null, 2)
writeFileSync('employees.json', jsonOutput, 'utf8')
// Read back
const parsed = JSON.parse(readFileSync('employees.json', 'utf8'))
console.log(Array.isArray(parsed)) // true
console.log(parsed[0].name) // "محمد الأحمد"أدوات ذات صلة
Alex is a front-end and Node.js developer with extensive experience building web applications and developer tooling. He is passionate about web standards, browser APIs, and the JavaScript ecosystem. In his spare time he contributes to open-source projects and writes about modern JavaScript patterns, performance optimisation, and everything related to the web platform.
Sophie is a full-stack developer focused on TypeScript across the entire stack — from React frontends to Express and Fastify backends. She has a particular interest in type-safe API design, runtime validation, and the patterns that make large JavaScript codebases stay manageable. She writes about TypeScript idioms, Node.js internals, and the ever-evolving JavaScript module ecosystem.