使用csv-writer导出嵌套JSON为CSV时嵌套字段无输出问题
问题
使用csv-writer将MongoDB中已关联(populate)的嵌套JSON数据转换为CSV时遇到问题:当csvFields仅配置{ id: 'title', title: 'Title' }这类顶层字段时,CSV生成正常;但配置嵌套字段{ id: 'task.model', title: 'task' }时,该字段无输出。已通过populate('task')关联了task文档,询问是否需要在传入writeRecords前处理文档。
使用的npm包:csv-writer
代码示例
let totalCount = await req.list.model.find(where).countDocuments(); const batchSize = 50; const batchCount = Math.ceil(totalCount / batchSize); const csvFields = [ { id: 'title', title: 'Title' }, { id: 'task.model', title: 'task' }, // 无输出 ]; // 尝试解决嵌套字段显示问题 for (let i = 0; i < 1; i++) { let batch = await req.list.model.find(where).skip(batchSize * i).populate('task').limit(2); const csvWriter = createCsvWriter({ header: csvFields, path: `batch_${i}.csv`, }); csvWriter.writeRecords(batch) .then(() => console.log('csv written successfully')) .catch((error) => console.log(error)); };
控制台输出的batch文档JSON
[ { title: 'Dummy Task', starred: false, isTicket: true, isDraft: false, isDeleted: true, closed: false, logs: [ 60c891a95372b913afdc8ed2, 60c893c75372b913afdc93c2 ], created_at: 2021-06-15T11:40:25.122Z, updated_at: 2021-06-15T11:49:27.261Z, _id: 60c891a95372b913afdc8ed1, ticketTat: null, closed_at: null, city: 60c70bf72fa78254cbf4d410, region: 60c70bf72fa78254cbf4d40f, camera: 60c891085372b913afdc8d82, schedule: 60c891505372b913afdc8e2c, company: 60c70b882fa78254cbf4d3e7, task: { complianceType: [Object], monitoringType: 'Self', monitoringMode: 'Watch', status: true, created_at: 2022-03-30T12:56:46.619Z, updated_at: 2022-03-30T12:56:46.619Z, _id: 60c890c85372b913afdc8d71, checklist: 604217fb6f4c96665e780f0a, referenceId: 604710f054fca40eadffe735, model: ' Task', endpoint: '', taskType: 'Artificial Intelligence', actionType: 'Activity Detection', recordingType: 'Video', taggedMediaType: [], cooldownPeriod: '180', tat: null, tag: null, slug: 'wopipe', threshold: [], pushType: [Array], __v: 0, faceRecognition: false, version: '1', incidentInfo: [], thirdPartyIntegration: null, modelType: [Object], optimalRangeTitle: 'Ideal Value' }, location: 60c70bf72fa78254cbf4d411, taskType: 'Artificial Intelligence', checklist: 604217fb6f4c96665e780f0a, assigned_at: 2021-06-15T11:40:25.122Z, __v: 0, confidence: 'high', uid: '7f57f0e0-05e4-4338-902a-a8b3cc1934bf', metadata: { task: 'Task', city: 'Buffalo', location: 'Lower West Side', region: 'New York', checklist: 'd1', schedule: 'Simulation', raisedBy: '' }, timezone: 'Pacific/Honolulu', modelType: { type: 'process-based', label: 'Process based' }, incidentInfo: [], assignTask: 60c891545372b913afdc8e36, raisedBy: null, archived: true, personDetected: [] }, { title: 'Dummy2Task', starred: false, isTicket: true, isDraft: false, isDeleted: true, closed: false, logs: [ 60c893b45372b913afdc93ad, 60c893c75372b913afdc93c3 ], created_at: 2021-06-15T11:49:08.898Z, updated_at: 2021-06-15T11:49:27.261Z, _id: 60c893b45372b913afdc93ac, raisedFrom: 'Artificial Intelligence', eventStatus: '', ticketNo: '002', ticketStatus: 60c70b882fa78254cbf4d3f3, ticketTag: 60c70b882fa78254cbf4d3f2, ticketTat: null, closed_at: null, city: 60c70bf72fa78254cbf4d410, region: 60c70bf72fa78254cbf4d40f, camera: 60c891085372b913afdc8d82, schedule: 60c891505372b913afdc8e2c, company: 60c70b882fa78254cbf4d3e7, task: { complianceType: [Object], monitoringType: 'Self', monitoringMode: 'Watch', status: true, created_at: 2022-03-30T12:56:46.619Z, updated_at: 2022-03-30T12:56:46.619Z, _id: 60c890c85372b913afdc8d71, checklist: 604217fb6f4c96665e780f0a, referenceId: 604710f054fca40eadffe735, model: 'Task2', endpoint: '', taskType: 'Artificial Intelligence', actionType: 'Activity Detection', recordingType: 'Video', taggedMediaType: [], cooldownPeriod: '180', tat: null, tag: null, slug: 'wopipe', threshold: [], pushType: [Array], company: 60c70b882fa78254cbf4d3e7, __v: 0, faceRecognition: false, version: '1', incidentInfo: [], thirdPartyIntegration: null, modelType: [Object], optimalRangeTitle: 'Ideal Value' }, location: 60c70bf72fa78254cbf4d411, taskType: 'Artificial Intelligence', checklist: 604217fb6f4c96665e780f0a, assigned_at: 2021-06-15T11:49:08.898Z, __v: 0, confidence: 'high', uid: '14b2b91f-308d-43be-8f5a-d9269b9139a6', metadata: { task: 'Task2', city: 'Buffalo', location: 'Lower West Side', region: 'New York', checklist: 'Pipe', schedule: 'Simulation2', raisedBy: '' }, timezone: 'Pacific/Honolulu', modelType: { type: 'process-based', label: 'Process based' }, incidentInfo: [], assignTask: 60c891545372b913afdc8e36, raisedBy: null, archived: true, personDetected: [] } ]
解决方案
是的,必须在传入writeRecords前预处理文档,因为csv-writer本身不支持通过点分隔符直接访问嵌套字段,它只能识别顶层属性。
处理方式1:手动提取嵌套字段到顶层
在获取到batch数据后,遍历每个文档,将嵌套的task.model提取为顶层属性,比如命名为task_model,然后修改csvFields的id为这个顶层属性名:
let totalCount = await req.list.model.find(where).countDocuments(); const batchSize = 50; const batchCount = Math.ceil(totalCount / batchSize); const csvFields = [ { id: 'title', title: 'Title' }, { id: 'task_model', title: 'task' }, // 修改为顶层属性名 ]; for (let i = 0; i < 1; i++) { let batch = await req.list.model.find(where).skip(batchSize * i).populate('task').limit(2); // 预处理数据:提取嵌套字段到顶层 const processedBatch = batch.map(item => ({ ...item.toObject(), // 把Mongoose文档转成普通对象 task_model: item.task?.model || '' // 提取task.model,处理可能的空值 })); const csvWriter = createCsvWriter({ header: csvFields, path: `batch_${i}.csv`, }); csvWriter.writeRecords(processedBatch) .then(() => console.log('csv written successfully')) .catch((error) => console.log(error)); };
处理方式2:使用csv-writer的转换函数
在创建csvWriter时,通过transform选项自定义每个记录的字段映射,这样不需要修改原数据结构:
let totalCount = await req.list.model.find(where).countDocuments(); const batchSize = 50; const batchCount = Math.ceil(totalCount / batchSize); const csvFields = [ { id: 'title', title: 'Title' }, { id: 'task', title: 'task' }, // 自定义id ]; for (let i = 0; i < 1; i++) { let batch = await req.list.model.find(where).skip(batchSize * i).populate('task').limit(2); const csvWriter = createCsvWriter({ header: csvFields, path: `batch_${i}.csv`, // 自定义转换函数,映射嵌套字段 transform: (record) => ({ title: record.title, task: record.task?.model || '' }) }); csvWriter.writeRecords(batch) .then(() => console.log('csv written successfully')) .catch((error) => console.log(error)); };
注意事项
- 如果使用Mongoose查询结果,记得用
toObject()把Mongoose文档转换为普通JavaScript对象,避免嵌套属性访问异常。 - 添加
?.可选链操作符处理task为null或undefined的情况,防止报错。
内容的提问来源于stack exchange,提问作者Mohammad Zuha Khalid
相关产品推荐
相关产品推荐

