JSON与CSV数据转换Skill json-and-csv-data-transformation

这个技能用于在JSON、CSV等数据格式之间进行转换,支持过滤、提取、映射、扁平化嵌套结构和聚合操作。适用于数据处理工作流,如API响应处理、ETL流程、数据清洗和报表生成,提升数据分析和集成效率。关键词:JSON转换, CSV转换, 数据过滤, 数据扁平化, ETL, 数据处理, 数据工程, API集成。

ETL开发 0 次安装 0 次浏览 更新于 3/22/2026

name: json-and-csv-data-transformation description: “在JSON、CSV和其他格式之间转换数据,包括过滤、映射和扁平化。使用场景:(1) 将API响应转换为CSV,(2) 处理数据管道,(3) 提取特定字段,或 (4) 扁平化嵌套结构。”

JSON和CSV数据转换

在JSON、CSV和其他格式之间转换数据。过滤、映射、扁平化嵌套对象,并重塑数据以进行分析、报告和API集成。

何时使用

  • 使用场景1:当用户要求将数据在JSON和CSV格式之间转换时
  • 使用场景2:当需要从数据中过滤、提取或转换特定字段时
  • 使用场景3:用于将嵌套JSON结构扁平化为表格格式
  • 使用场景4:当处理API响应以进行分析或报告时

所需工具/API

  • jq — 命令行JSON处理器(用于JSON操作的基本工具)
  • csvkit — CSV工具套件(csvjson、csvcut、csvgrep等)
  • 无需外部API

安装选项:

# Ubuntu/Debian
sudo apt-get install -y jq csvkit

# macOS
brew install jq csvkit

# Node.js(原生支持,基础操作无需包)
# 对于高级CSV解析:npm install csv-parse csv-stringify

技能

json_to_csv

将JSON数组转换为CSV格式。

# 简单JSON数组到CSV
echo '[{"name":"Alice","age":30},{"name":"Bob","age":25}]' | jq -r '(.[0] | keys_unsorted) as $keys | $keys, (map([.[ $keys[] ]]) | .[] | @csv)'

# JSON文件到CSV文件
jq -r '(.[0] | keys_unsorted) as $keys | $keys, (map([.[ $keys[] ]]) | .[] | @csv)' data.json > output.csv

# JSON到CSV并指定字段
jq -r '.[] | [.id, .name, .email] | @csv' users.json

# 使用csvkit(更简单的语法)
cat data.json | in2csv -f json > output.csv

Node.js:

function jsonToCSV(jsonArray) {
  if (!Array.isArray(jsonArray) || jsonArray.length === 0) {
    return '';
  }
  
  // 从第一个对象获取表头
  const headers = Object.keys(jsonArray[0]);
  
  // 转义CSV值
  const escape = (val) => {
    if (val === null || val === undefined) return '';
    const str = String(val);
    if (str.includes(',') || str.includes('"') || str.includes('
')) {
      return `"${str.replace(/"/g, '""')}"`;
    }
    return str;
  };
  
  // 构建CSV
  const headerRow = headers.join(',');
  const dataRows = jsonArray.map(obj =>
    headers.map(header => escape(obj[header])).join(',')
  );
  
  return [headerRow, ...dataRows].join('
');
}

// 用法
// const data = [
//   { name: 'Alice', age: 30, city: 'New York' },
//   { name: 'Bob', age: 25, city: 'San Francisco' }
// ];
// console.log(jsonToCSV(data));

csv_to_json

将CSV转换为JSON数组。

# CSV到JSON
csvjson data.csv

# CSV到JSON并美化打印
csvjson data.csv | jq '.'

# CSV到对象数组的JSON
csvjson --stream data.csv

# CSV文件到JSON文件
csvjson input.csv > output.json

# 使用纯jq(如果表头在第一行)
jq -Rsn '[inputs | split(",") | {name: .[0], age: .[1], city: .[2]}]' < data.csv

Node.js:

function csvToJSON(csvString) {
  const lines = csvString.trim().split('
');
  if (lines.length < 2) return [];
  
  // 解析CSV值(处理引号)
  const parseCSVValue = (val) => {
    val = val.trim();
    if (val.startsWith('"') && val.endsWith('"')) {
      return val.slice(1, -1).replace(/""/g, '"');
    }
    return val;
  };
  
  // 分割CSV行(基础实现)
  const splitCSVLine = (line) => {
    const result = [];
    let current = '';
    let inQuotes = false;
    
    for (let i = 0; i < line.length; i++) {
      const char = line[i];
      
      if (char === '"') {
        inQuotes = !inQuotes;
        current += char;
      } else if (char === ',' && !inQuotes) {
        result.push(parseCSVValue(current));
        current = '';
      } else {
        current += char;
      }
    }
    result.push(parseCSVValue(current));
    return result;
  };
  
  const headers = splitCSVLine(lines[0]);
  const data = lines.slice(1).map(line => {
    const values = splitCSVLine(line);
    const obj = {};
    headers.forEach((header, i) => {
      obj[header] = values[i] || '';
    });
    return obj;
  });
  
  return data;
}

// 用法
// const csv = `name,age,city
// Alice,30,New York
// Bob,25,"San Francisco"`;
// console.log(JSON.stringify(csvToJSON(csv), null, 2));

filter_and_extract_json

从JSON中过滤和提取特定字段。

# 提取特定字段
jq '.[] | {name: .name, email: .email}' users.json

# 按条件过滤
jq '.[] | select(.age > 25)' users.json

# 过滤并提取
jq '[.[] | select(.active == true) | {id: .id, name: .name}]' data.json

# 提取嵌套字段
jq '.[] | {name: .name, street: .address.street, city: .address.city}' data.json

# 获取单个字段的数组
jq '.[].name' users.json

# 多条件过滤
jq '.[] | select(.age > 20 and .country == "USA")' users.json

# 映射和转换值
jq '.[] | .price = (.price * 1.1)' products.json

Node.js:

function filterAndExtractJSON(data, options) {
  const { filter, extract } = options;
  
  let result = Array.isArray(data) ? data : [data];
  
  // 应用过滤函数
  if (filter) {
    result = result.filter(filter);
  }
  
  // 提取特定字段
  if (extract) {
    result = result.map(item => {
      const extracted = {};
      extract.forEach(field => {
        // 支持点表示法的嵌套字段
        const value = field.split('.').reduce((obj, key) => obj?.[key], item);
        extracted[field] = value;
      });
      return extracted;
    });
  }
  
  return result;
}

// 用法
// const users = [
//   { id: 1, name: 'Alice', age: 30, address: { city: 'NYC' } },
//   { id: 2, name: 'Bob', age: 25, address: { city: 'SF' } },
//   { id: 3, name: 'Charlie', age: 35, address: { city: 'LA' } }
// ];
// 
// const result = filterAndExtractJSON(users, {
//   filter: user => user.age > 25,
//   extract: ['name', 'age', 'address.city']
// });
// console.log(result);

flatten_nested_json

将嵌套JSON对象扁平化为扁平结构。

# 使用jq扁平化嵌套JSON
jq '[.[] | {id: .id, name: .name, street: .address.street, city: .address.city, zip: .address.zip}]' users.json

# 扁平化所有嵌套字段并自定义分隔符
jq '[.[] | to_entries | map({key: .key, value: .value}) | from_entries]' data.json

# 扁平化深层嵌套结构
jq 'recurse | select(type != "object" and type != "array")' complex.json

Node.js:

function flattenJSON(obj, prefix = '', separator = '.') {
  const flattened = {};
  
  for (const key in obj) {
    const value = obj[key];
    const newKey = prefix ? `${prefix}${separator}${key}` : key;
    
    if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
      // 递归扁平化嵌套对象
      Object.assign(flattened, flattenJSON(value, newKey, separator));
    } else if (Array.isArray(value)) {
      // 将数组转换为字符串或扁平化每个项
      flattened[newKey] = JSON.stringify(value);
    } else {
      flattened[newKey] = value;
    }
  }
  
  return flattened;
}

// 用法
// const nested = {
//   id: 1,
//   name: 'Alice',
//   address: {
//     street: '123 Main St',
//     city: 'NYC',
//     coordinates: { lat: 40.7, lon: -74.0 }
//   },
//   tags: ['user', 'active']
// };
// console.log(flattenJSON(nested));
// 输出:{
//   id: 1,
//   name: 'Alice',
//   'address.street': '123 Main St',
//   'address.city': 'NYC',
//   'address.coordinates.lat': 40.7,
//   'address.coordinates.lon': -74.0,
//   tags: '["user","active"]'
// }

transform_csv_data

转换和操作CSV数据。

# 选择特定列
csvcut -c name,email,age users.csv

# 按值过滤行
csvgrep -c age -r "^[3-9][0-9]$" users.csv  # 年龄 >= 30

# 排序CSV
csvsort -c age -r users.csv  # 按年龄反向排序

# 移除重复行
csvcut -c name,email users.csv | uniq

# 组合:过滤、选择列、排序
csvgrep -c country -m "USA" users.csv | csvcut -c name,age | csvsort -c age

# 添加计算列(需要csvpy或awk)
awk -F',' 'BEGIN{OFS=","} NR==1{print $0,"total"} NR>1{print $0,$2*$3}' data.csv

# 按列合并两个CSV文件
csvjoin -c id users.csv orders.csv

Node.js:

function transformCSV(csvData, transformations) {
  const { selectColumns, filterRows, sortBy } = transformations;
  
  // 将CSV解析为对象
  const data = csvToJSON(csvData);
  
  let result = data;
  
  // 过滤行
  if (filterRows) {
    result = result.filter(filterRows);
  }
  
  // 选择列
  if (selectColumns) {
    result = result.map(row => {
      const selected = {};
      selectColumns.forEach(col => {
        selected[col] = row[col];
      });
      return selected;
    });
  }
  
  // 排序
  if (sortBy) {
    const { column, reverse } = sortBy;
    result.sort((a, b) => {
      const aVal = a[column];
      const bVal = b[column];
      const comparison = aVal > bVal ? 1 : aVal < bVal ? -1 : 0;
      return reverse ? -comparison : comparison;
    });
  }
  
  // 转换回CSV
  return jsonToCSV(result);
}

// 用法
// const csv = `name,age,country
// Alice,30,USA
// Bob,25,Canada
// Charlie,35,USA`;
//
// const transformed = transformCSV(csv, {
//   filterRows: row => row.country === 'USA',
//   selectColumns: ['name', 'age'],
//   sortBy: { column: 'age', reverse: true }
// });
// console.log(transformed);

aggregate_and_group_json

聚合和分组JSON数据(类似于SQL GROUP BY)。

# 按字段分组并计数
jq 'group_by(.country) | map({country: .[0].country, count: length})' users.json

# 按组求和值
jq 'group_by(.category) | map({category: .[0].category, total: map(.price) | add})' products.json

# 按组求平均值
jq 'group_by(.department) | map({department: .[0].department, avg_salary: (map(.salary) | add / length)})' employees.json

# 多重聚合
jq 'group_by(.region) | map({
  region: .[0].region,
  count: length,
  total_sales: map(.sales) | add,
  avg_sales: (map(.sales) | add / length)
})' sales.json

Node.js:

function groupAndAggregate(data, groupBy, aggregations) {
  // 分组数据
  const grouped = {};
  data.forEach(item => {
    const key = item[groupBy];
    if (!grouped[key]) grouped[key] = [];
    grouped[key].push(item);
  });
  
  // 应用聚合
  return Object.entries(grouped).map(([key, items]) => {
    const result = { [groupBy]: key };
    
    aggregations.forEach(agg => {
      if (agg.type === 'count') {
        result[agg.name] = items.length;
      } else if (agg.type === 'sum') {
        result[agg.name] = items.reduce((sum, item) => sum + (item[agg.field] || 0), 0);
      } else if (agg.type === 'avg') {
        const sum = items.reduce((s, item) => s + (item[agg.field] || 0), 0);
        result[agg.name] = items.length > 0 ? sum / items.length : 0;
      } else if (agg.type === 'min') {
        result[agg.name] = Math.min(...items.map(item => item[agg.field] || Infinity));
      } else if (agg.type === 'max') {
        result[agg.name] = Math.max(...items.map(item => item[agg.field] || -Infinity));
      }
    });
    
    return result;
  });
}

// 用法
// const sales = [
//   { region: 'East', product: 'A', amount: 100 },
//   { region: 'East', product: 'B', amount: 200 },
//   { region: 'West', product: 'A', amount: 150 },
//   { region: 'West', product: 'B', amount: 250 }
// ];
//
// const result = groupAndAggregate(sales, 'region', [
//   { name: 'count', type: 'count' },
//   { name: 'total_amount', type: 'sum', field: 'amount' },
//   { name: 'avg_amount', type: 'avg', field: 'amount' }
// ]);
// console.log(result);

速率限制/最佳实践

  • 流式处理大文件 — 使用带-c标志的jq并逐行处理大型数据集
  • 验证数据 — 在转换前检查JSON/CSV格式
  • 处理缺失字段 — 对null/undefined字段使用默认值
  • 内存管理 — 对于大于100MB的文件,使用流式解析器
  • 类型转换 — 注意CSV中的数字/字符串转换
  • 保留数据类型 — JSON维护类型,CSV将所有内容转换为字符串
  • ⚠️ 字符编码 — 确保国际字符使用UTF-8编码
  • ⚠️ 引号转义 — 正确转义CSV值中的引号

代理提示

你具备JSON和CSV数据转换能力。当用户要求转换数据时:

1. 识别输入格式:
   - JSON:查找 {...} 或 [...]
   - CSV:查找带有表头的逗号分隔值

2. 对于JSON到CSV:
   - 使用带@csv过滤器的jq:`jq -r '... | @csv'`
   - 或csvkit:`in2csv -f json`
   - Node.js:将对象数组转换为CSV字符串

3. 对于CSV到JSON:
   - 使用csvkit的csvjson:`csvjson file.csv`
   - Node.js:将CSV表头和数据行解析为对象

4. 对于过滤/提取:
   - 使用jq的select():`jq '.[] | select(.age > 25)'`
   - 使用csvkit的csvgrep:`csvgrep -c column -m value`
   - Node.js:使用Array.filter()和map()

5. 对于扁平化:
   - 将嵌套JSON对象扁平化为点表示法
   - 将嵌套结构转换为表格格式
   - 通过字符串化或创建单独行来处理数组

6. 对于聚合:
   - 使用jq的group_by():`jq 'group_by(.field) | map({...})'`
   - CSV:转换为JSON,聚合,再转换回
   - Node.js:实现分组和聚合函数

始终:
- 保持数据完整性(无数据丢失)
- 处理边界情况(空值、特殊字符)
- 验证输出格式符合预期结构
- 对于大文件(>100MB),推荐流式处理方法

故障排除

错误:“parse error: Invalid numeric literal”

  • 症状:jq无法解析JSON
  • 解决方案:使用jq empty file.json验证JSON格式,修复语法错误

CSV列未对齐:

  • 症状:转换后数据出现在错误的列中
  • 解决方案:检查数据中未转义的逗号,确保引号正确转义

jq输出为空:

  • 症状:jq无返回结果
  • 解决方案:检查过滤表达式语法,验证数据结构匹配过滤条件

CSV中特殊字符损坏:

  • 症状:非ASCII字符显示乱码
  • 解决方案:确保UTF-8编码:iconv -f UTF-8 -t UTF-8 file.csv

大文件内存错误:

  • 症状:进程内存不足
  • 解决方案:使用流式模式:jq -c或Node.js流进行逐行处理

JSON未转换为扁平CSV:

  • 症状:嵌套对象创建了复杂的CSV结构
  • 解决方案:在转换为CSV前先扁平化JSON

另请参见