n8n 数据处理与转换 - 让数据流动起来
在工作流中,数据就像血液一样在各个节点间流动。掌握数据处理技巧是构建高效工作流的关键。今天我们来深入学习 n8n 的数据处理和转换方法。
数据结构基础
n8n 数据格式
n8n 中的数据以 JSON 格式在节点间传递,基本结构如下:
json
[
{
"json": {
"name": "张三",
"age": 30,
"email": "zhangsan@example.com"
},
"binary": {},
"pairedItem": {
"item": 0
}
}
]
关键字段说明
json
:主要数据内容binary
:二进制数据(文件、图片等)pairedItem
:数据来源追踪
数据类型
基本类型
- String:字符串
- Number:数字
- Boolean:布尔值
- Array:数组
- Object:对象
复杂类型
- Date:日期时间
- Binary:二进制数据
- Null/Undefined:空值
Set 节点详解
Set 节点是最常用的数据处理节点,用于设置、修改或删除数据字段。
基本操作
添加字段
json
{
"values": {
"string": [
{
"name": "status",
"value": "processed"
}
],
"number": [
{
"name": "priority",
"value": 1
}
],
"boolean": [
{
"name": "isActive",
"value": true
}
]
}
}
使用表达式
json
{
"values": {
"string": [
{
"name": "fullName",
"value": "={{ $json.firstName }} {{ $json.lastName }}"
},
{
"name": "processedAt",
"value": "={{ $now }}"
}
]
}
}
高级用法
条件设置
json
{
"values": {
"string": [
{
"name": "category",
"value": "={{ $json.age >= 18 ? 'adult' : 'minor' }}"
}
]
}
}
数组处理
json
{
"values": {
"string": [
{
"name": "tags",
"value": "={{ $json.categories.join(', ') }}"
}
]
}
}
对象操作
json
{
"values": {
"object": [
{
"name": "address",
"value": {
"street": "={{ $json.street }}",
"city": "={{ $json.city }}",
"country": "中国"
}
}
]
}
}
Code 节点深入
Code 节点提供了最大的灵活性,可以执行复杂的数据处理逻辑。
基本语法
javascript
// 处理单个数据项
const item = items[0].json;
// 数据转换
const processedData = {
id: item.id,
name: item.name.trim().toLowerCase(),
email: item.email.toLowerCase(),
createdAt: new Date().toISOString()
};
return [{
json: processedData
}];
批量处理
javascript
// 处理多个数据项
const processedItems = items.map(item => {
const data = item.json;
return {
json: {
...data,
processed: true,
processedAt: new Date().toISOString(),
hash: require('crypto')
.createHash('md5')
.update(data.email)
.digest('hex')
}
};
});
return processedItems;
数据验证
javascript
// 数据验证和清洗
const validItems = [];
const invalidItems = [];
items.forEach(item => {
const data = item.json;
// 验证必填字段
if (!data.email || !data.name) {
invalidItems.push({
...item,
json: {
...data,
error: 'Missing required fields'
}
});
return;
}
// 验证邮箱格式
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(data.email)) {
invalidItems.push({
...item,
json: {
...data,
error: 'Invalid email format'
}
});
return;
}
// 数据清洗
validItems.push({
json: {
id: data.id,
name: data.name.trim(),
email: data.email.toLowerCase().trim(),
phone: data.phone ? data.phone.replace(/\D/g, '') : null,
isValid: true
}
});
});
// 返回有效数据,无效数据可以通过错误处理流程处理
return validItems;
外部库使用
javascript
// 使用内置库
const crypto = require('crypto');
const moment = require('moment');
const processedItems = items.map(item => {
const data = item.json;
return {
json: {
...data,
hash: crypto.createHash('sha256').update(data.id).digest('hex'),
formattedDate: moment(data.createdAt).format('YYYY-MM-DD HH:mm:ss'),
timestamp: Date.now()
}
};
});
return processedItems;
数据转换实战
JSON 数据扁平化
javascript
// 将嵌套对象扁平化
function flattenObject(obj, prefix = '') {
const flattened = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const newKey = prefix ? `${prefix}.${key}` : key;
if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
Object.assign(flattened, flattenObject(obj[key], newKey));
} else {
flattened[newKey] = obj[key];
}
}
}
return flattened;
}
const processedItems = items.map(item => {
const flatData = flattenObject(item.json);
return {
json: flatData
};
});
return processedItems;
CSV 数据处理
javascript
// 处理 CSV 格式数据
const csvData = items[0].json.csvContent;
const lines = csvData.split('\n');
const headers = lines[0].split(',').map(h => h.trim());
const processedData = lines.slice(1)
.filter(line => line.trim()) // 过滤空行
.map(line => {
const values = line.split(',').map(v => v.trim());
const obj = {};
headers.forEach((header, index) => {
obj[header] = values[index] || '';
});
return { json: obj };
});
return processedData;
API 响应数据处理
javascript
// 处理 API 响应数据
const apiResponse = items[0].json;
// 提取分页信息
const pagination = {
currentPage: apiResponse.page,
totalPages: apiResponse.total_pages,
totalItems: apiResponse.total,
hasNext: apiResponse.page < apiResponse.total_pages
};
// 处理数据项
const processedItems = apiResponse.data.map(item => ({
json: {
id: item.id,
title: item.title,
description: item.description,
createdAt: new Date(item.created_at).toISOString(),
author: {
id: item.author.id,
name: item.author.name,
email: item.author.email
},
tags: item.tags || [],
isPublished: item.status === 'published'
}
}));
// 添加分页信息到第一个项目
if (processedItems.length > 0) {
processedItems[0].json.pagination = pagination;
}
return processedItems;
Function 节点应用
Function 节点适合简单的数据转换操作。
字符串处理
javascript
// 格式化姓名
return {
fullName: $json.firstName + ' ' + $json.lastName,
initials: ($json.firstName.charAt(0) + $json.lastName.charAt(0)).toUpperCase()
};
数字计算
javascript
// 计算折扣价格
return {
originalPrice: $json.price,
discount: $json.discount || 0,
finalPrice: $json.price * (1 - ($json.discount || 0) / 100),
savings: $json.price * (($json.discount || 0) / 100)
};
日期处理
javascript
// 日期格式化
const date = new Date($json.timestamp);
return {
originalTimestamp: $json.timestamp,
formattedDate: date.toLocaleDateString('zh-CN'),
formattedTime: date.toLocaleTimeString('zh-CN'),
dayOfWeek: ['日', '一', '二', '三', '四', '五', '六'][date.getDay()],
isWeekend: date.getDay() === 0 || date.getDay() === 6
};
数据聚合与分组
使用 Code 节点进行分组
javascript
// 按类别分组数据
const groupedData = {};
items.forEach(item => {
const data = item.json;
const category = data.category || 'uncategorized';
if (!groupedData[category]) {
groupedData[category] = [];
}
groupedData[category].push(data);
});
// 转换为数组格式
const result = Object.keys(groupedData).map(category => ({
json: {
category: category,
items: groupedData[category],
count: groupedData[category].length,
totalValue: groupedData[category].reduce((sum, item) => sum + (item.value || 0), 0)
}
}));
return result;
数据统计
javascript
// 计算统计信息
const values = items.map(item => item.json.value || 0);
const count = values.length;
const sum = values.reduce((a, b) => a + b, 0);
const avg = count > 0 ? sum / count : 0;
const min = Math.min(...values);
const max = Math.max(...values);
// 计算中位数
const sorted = values.sort((a, b) => a - b);
const median = count % 2 === 0
? (sorted[count / 2 - 1] + sorted[count / 2]) / 2
: sorted[Math.floor(count / 2)];
return [{
json: {
statistics: {
count,
sum,
average: avg,
minimum: min,
maximum: max,
median,
range: max - min
},
data: items.map(item => item.json)
}
}];
错误处理与数据验证
数据验证模式
javascript
// 定义验证规则
const validationRules = {
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
phone: /^1[3-9]\d{9}$/,
required: ['name', 'email']
};
const processedItems = items.map(item => {
const data = item.json;
const errors = [];
// 检查必填字段
validationRules.required.forEach(field => {
if (!data[field] || data[field].toString().trim() === '') {
errors.push(`${field} is required`);
}
});
// 验证邮箱格式
if (data.email && !validationRules.email.test(data.email)) {
errors.push('Invalid email format');
}
// 验证手机号格式
if (data.phone && !validationRules.phone.test(data.phone)) {
errors.push('Invalid phone format');
}
return {
json: {
...data,
isValid: errors.length === 0,
errors: errors
}
};
});
return processedItems;
异常处理
javascript
try {
const processedItems = items.map(item => {
const data = item.json;
// 可能出错的操作
const parsedDate = new Date(data.dateString);
if (isNaN(parsedDate.getTime())) {
throw new Error(`Invalid date: ${data.dateString}`);
}
return {
json: {
...data,
parsedDate: parsedDate.toISOString(),
processed: true
}
};
});
return processedItems;
} catch (error) {
// 返回错误信息
return [{
json: {
error: true,
message: error.message,
originalData: items
}
}];
}
性能优化技巧
批量处理优化
javascript
// 分批处理大量数据
const BATCH_SIZE = 100;
const allItems = items;
const batches = [];
for (let i = 0; i < allItems.length; i += BATCH_SIZE) {
const batch = allItems.slice(i, i + BATCH_SIZE);
batches.push(batch);
}
// 处理每个批次
const processedBatches = batches.map((batch, batchIndex) => {
const processedBatch = batch.map(item => {
// 处理逻辑
return {
json: {
...item.json,
batchIndex,
processed: true
}
};
});
return processedBatch;
});
// 合并结果
return processedBatches.flat();
内存优化
javascript
// 避免创建大量临时对象
const processedItems = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
const data = item.json;
// 直接修改现有对象而不是创建新对象
data.processed = true;
data.processedAt = Date.now();
processedItems.push(item);
}
return processedItems;
小结
数据处理是 n8n 工作流的核心技能,主要要点:
- 理解数据结构:掌握 n8n 的数据格式和传递机制
- 选择合适工具:Set 节点用于简单操作,Code 节点用于复杂逻辑
- 数据验证:确保数据质量和完整性
- 错误处理:优雅地处理异常情况
- 性能优化:合理处理大量数据
下一篇文章,我们将学习条件逻辑和分支控制,这是构建智能工作流的重要技术。
记住,好的数据处理不仅要功能正确,还要考虑性能、可维护性和错误处理。多练习不同类型的数据转换,你会发现数据处理的乐趣所在。