【n8n教程】:掌握n8n数据处理 - 从基础到实战
在使用n8n进行工作流自动化时,数据处理是核心技能。无论是从API获取数据、转换格式、还是在不同节点之间映射数据,理解n8n的数据结构都是必不可少的。本教程将带你从零开始理解n8n的数据流、数据映射和数据转换,并通过实际案例帮助你快速掌握这一关键能力。
第一部分:理解n8n的数据结构
什么是n8n中的数据?
n8n中的数据是指节点接收和处理的信息。在底层,所有在节点之间传递的数据都遵循一个统一的结构。
核心数据结构
n8n中所有数据都以JSON对象数组的形式传递。每个数据项都有这样的结构:
[
{
"json": {
"字段名": "值",
"另一个字段": "另一个值"
}
},
{
"json": {
"字段名": "值2",
"另一个字段": "另一个值2"
}
}
]
关键概念:
| 概念 | 说明 | 示例 |
|---|---|---|
| Item(项目) | 数据的单个单元 | 一条用户记录、一个订单 |
| json 键 | 包含实际数据的对象 | { "name": "John", "age": 30 } |
| 数组 | 多个item组成的集合 | [item1, item2, item3] |
简单示例
假设你从HTTP请求节点获取了一个用户列表,数据可能是这样的:
[
{
"json": {
"id": 1,
"name": "李四",
"email": "lisi@example.com",
"status": "active"
}
},
{
"json": {
"id": 2,
"name": "王五",
"email": "wangwu@example.com",
"status": "inactive"
}
}
]
这个数组包含了2个item,每个item代表一个用户。
第二部分:数据在节点中的流动
节点如何处理多个数据项
n8n中的每个节点都可以逐个处理多个数据项。这是n8n设计的核心特性。
例子:创建Trello卡片
如果你配置一个Trello节点来"创建卡片",并使用表达式 {{ $json.name }} 来设置卡片名称,当输入包含3个item时:
| 输入数据 | 操作 | 输出 |
|---|---|---|
| item1: name = "任务1" | 创建卡片 | 创建了名为"任务1"的卡片 |
| item2: name = "任务2" | 创建卡片 | 创建了名为"任务2"的卡片 |
| item3: name = "任务3" | 创建卡片 | 创建了名为"任务3"的卡片 |
最后,Trello中会有3张新卡片。
理解这意味着什么
这种设计让你可以:
- 批量处理数据:一次运行处理多条记录
- 避免重复编码:编写一次逻辑,自动应用于所有item
- 构建高效的工作流:无需循环节点就能处理列表
第三部分:数据映射 - 在节点间传递数据
什么是数据映射?
数据映射是指从前一个节点的输出中引用数据,并将其传递给下一个节点的过程。它不涉及数据转换,只是引用。
方法1:拖拽式映射(最直观)
这是初学者最推荐的方法:
步骤:
- 运行你的工作流以加载数据
- 打开需要映射数据的节点
- 在INPUT面板中找到你要使用的数据
- 点击并按住要映射的字段
- 拖拽到你要使用的参数字段中
- n8n会自动生成表达式
例子:
假设你的HTTP Request节点返回了这样的数据:
{
"json": {
"customer_name": "张三",
"customer_email": "zhangsan@example.com",
"order_total": 299.99
}
}
现在你要在Gmail节点中使用 customer_name。只需在INPUT面板中拖拽 customer_name 字段到Gmail的"收件人名称"参数,n8n会自动生成表达式:
{{ $json.customer_name }}
方法2:表达式编辑器(更灵活)
对于更复杂的需求,你可以直接在表达式编辑器中编写:
// 访问顶级字段
{{ $json.fieldName }}
// 访问嵌套字段
{{ $json.user.profile.email }}
// 访问数组元素
{{ $json.items[0].name }}
// 使用默认值
{{ $json.optionalField || 'default value' }}
实际示例:
// 组合多个字段
{{ $json.first_name }} {{ $json.last_name }}
// 结果:张三 李四
// 简单计算
{{ $json.price * $json.quantity }}
// 条件赋值
{{ $json.status === 'premium' ? $json.price * 0.9 : $json.price }}
理解嵌套数据映射
如果你的数据结构是这样的:
{
"json": {
"user": {
"profile": {
"name": "李四",
"email": "lisi@example.com"
}
},
"order": {
"id": 12345,
"items": ["商品1", "商品2"]
}
}
}
你可以这样访问:
| 需要的数据 | 表达式 | 结果 |
|---|---|---|
| 用户名 | {{ $json.user.profile.name }} | 李四 |
| 邮箱 | {{ $json.user.profile.email }} | lisi@example.com |
| 订单ID | {{ $json.order.id }} | 12345 |
| 第一个商品 | {{ $json.order.items[0] }} | 商品1 |
第四部分:数据转换
为什么需要数据转换?
从外部系统获取的数据格式可能与n8n的标准数据结构不兼容。你需要将其转换为n8n可以处理的格式。
常用的数据转换节点
| 节点 | 功能 | 使用场景 |
|---|---|---|
| Split Out | 将单个包含列表的item分成多个item | API返回了嵌套数组,需要逐个处理 |
| Aggregate | 将多个item组合成单个item或分组 | 需要汇总或合并数据 |
| Remove Duplicates | 删除重复的item | 清理重复数据 |
| Sort | 排序item | 按字段排序数据 |
| Limit | 限制item数量 | 只保留前N条记录 |
| Summarize | 类似Excel数据透视表的聚合 | 生成统计数据 |
实际示例:Split Out节点
场景: 你的API返回了这样的数据:
{
"json": {
"products": [
{ "id": 1, "name": "手机", "price": 3999 },
{ "id": 2, "name": "电脑", "price": 6999 },
{ "id": 3, "name": "平板", "price": 2999 }
]
}
}
问题: 这是1个item,但你需要为每个产品创建一条记录。
解决方案: 使用Split Out节点,选择要分割的字段为 products
输出: 3个item,每个代表一个产品
// Item 1
{ "json": { "id": 1, "name": "手机", "price": 3999 } }
// Item 2
{ "json": { "id": 2, "name": "电脑", "price": 6999 } }
// Item 3
{ "json": { "id": 3, "name": "平板", "price": 2999 } }
第五部分:使用代码处理数据
Function节点 vs Code节点
| 特性 | Function节点 | Code节点 |
|---|---|---|
| 语言 | JavaScript | JavaScript / Python |
| 复杂度 | 简单单行代码 | 更复杂的逻辑 |
| 自动处理 | 自动添加json键和数组 | 从0.166.0版本后也自动处理 |
| 文件大小 | 不可处理 | 可处理 |
Function节点示例
场景: 你需要为每个用户生成一个欢迎邮件,并创建一个新字段 greeting
// 输入:{ "json": { "name": "李四" } }
return {
json: {
...($json), // 保留原有字段
greeting: `尊敬的 ${$json.name},欢迎使用我们的服务!`,
processed_at: new Date().toISOString()
}
};
// 输出:
// {
// "json": {
// "name": "李四",
// "greeting": "尊敬的 李四,欢迎使用我们的服务!",
// "processed_at": "2025-12-04T14:02:00Z"
// }
// }
Code节点示例 - 处理多个item
// 处理所有输入item
const items = $input.all();
const result = items
.filter(item => item.json.age >= 18) // 筛选成年人
.map(item => ({
json: {
...item.json,
category: item.json.age < 30 ? '青年' : '中年',
verified: true
}
}));
return result;
重要提示: 从n8n 0.166.0版本开始,Function和Code节点会自动处理json键和数组包装,让代码编写更简洁。
第六部分:数据固定与编辑 - 开发时的利器
什么是数据固定(Data Pinning)?
数据固定是指保存节点的输出数据,并在后续测试中使用这个保存的数据,而不是每次都重新获取。
好处:
- 避免重复调用外部API(节省配额)
- 不依赖外部系统的可用性(离线测试)
- 保证测试数据一致性
- 加快开发速度
如何固定数据
- 运行节点以加载数据
- 在OUTPUT面板中找到Pin data按钮
- 点击Pin data
- 一个横幅会显示"This data is pinned"
现在,即使外部系统数据改变,下次运行时也会使用固定的数据。
如何编辑固定数据
有时你想测试不同的场景。你可以编辑固定的数据:
- 在OUTPUT面板中选择JSON标签
- 点击Edit按钮
- 修改JSON数据
- 点击Save保存
这对于测试边界情况非常有用:
- 测试空值
- 测试极端数字
- 测试长文本
- 测试错误情况
第七部分:项目链接 - 高级概念
什么是项目链接(Item Linking)?
当节点处理多个item时,n8n会自动跟踪每个输出item与哪个输入item相关。这就是项目链接。
自动项目链接规则
n8n会自动尝试链接item:
| 输入 | 输出 | 链接方式 |
|---|---|---|
| 1个 | 1个 | 输出链接到输入 |
| 1个 | 多个 | 所有输出都链接到这个输入 |
| 多个 | 多个(数量相等) | 按顺序一一对应 |
在表达式中访问链接的item
在表达式编辑器中,你可以访问之前节点中的item:
// 访问前一个节点中的链接item
{{ $item(0).$json.original_field }}
// 在多步工作流中回溯
{{ $item("NodeName").$json.fieldName }}
例子:
假设你有一个工作流:
- HTTP Request 获取用户
- Code节点 - 处理用户数据
- Google Sheets - 将数据写入
在Google Sheets节点中,如果你想引用HTTP Request节点中的原始用户名:
{{ $item("HTTP Request").$json.username }}
第八部分:数据表 - 在工作流中存储数据
什么是数据表?
数据表是n8n内置的轻量级数据存储,无需外部数据库就可以在工作流中存储和管理数据。
使用场景
- 存储重复运行检查标记(防止重复)
- 存储工作流之间的共享数据
- 创建查找表
- 保存AI工作流的评估数据
快速使用步骤
第一步:创建数据表
- 进入n8n项目
- 点击Data tables标签
- 点击Create Data table
- 输入表名并添加列
第二步:在工作流中使用
使用Data table节点来:
- 查询数据
- 插入新行
- 更新现有数据
例子:防止重复运行
// 在Data table中存储已处理的订单ID
// 每次运行时查询这个表,检查订单是否已处理
第九部分:实战案例 - 完整可执行工作流
场景介绍
你需要创建一个工作流,用来:
- 从模拟API获取产品列表
- 对产品进行转换和过滤
- 将数据分解为单个item
- 添加计算字段
- 输出最终结果
工作流代码
以下是一个完整的可导入工作流JSON:
{
"name": "Product Data Processing Workflow",
"nodes": [
{
"parameters": {},
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [250, 300]
},
{
"parameters": {
"jsCode": "return [\n {\n json: {\n products: [\n { id: 1, name: '手机', price: 3999, stock: 50, category: 'electronics' },\n { id: 2, name: '平板', price: 2999, stock: 30, category: 'electronics' },\n { id: 3, name: '充电器', price: 99, stock: 200, category: 'accessories' },\n { id: 4, name: '手机壳', price: 49, stock: 500, category: 'accessories' }\n ]\n }\n }\n];"
},
"name": "Simulate API Response",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [450, 300]
},
{
"parameters": {
"fieldToSplitOut": "products"
},
"name": "Split Products",
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [650, 300]
},
{
"parameters": {
"fields": {
"values": [
{
"name": "id",
"type": "nb:number",
"extractValue": "={{ $json.id }}"
},
{
"name": "name",
"type": "nb:string",
"extractValue": "={{ $json.name }}"
},
{
"name": "price",
"type": "nb:number",
"extractValue": "={{ $json.price }}"
},
{
"name": "stock",
"type": "nb:number",
"extractValue": "={{ $json.stock }}"
},
{
"name": "category",
"type": "nb:string",
"extractValue": "={{ $json.category }}"
},
{
"name": "in_stock",
"type": "nb:boolean",
"extractValue": "={{ $json.stock > 0 }}"
},
{
"name": "tax",
"type": "nb:number",
"extractValue": "={{ ($json.price * 0.13).toFixed(2) }}"
},
{
"name": "final_price",
"type": "nb:number",
"extractValue": "={{ ($json.price + ($json.price * 0.13)).toFixed(2) }}"
}
]
}
},
"name": "Add Calculated Fields",
"type": "n8n-nodes-base.set",
"typeVersion": 3,
"position": [850, 300]
},
{
"parameters": {
"jsCode": "// 只返回库存不足的产品\nif ($json.stock < 100) {\n return { json: $json };\n}"
},
"name": "Filter Low Stock",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1050, 300]
}
],
"connections": {
"Manual Trigger": {
"main": [
[
{
"node": "Simulate API Response",
"type": "main",
"index": 0
}
]
]
},
"Simulate API Response": {
"main": [
[
{
"node": "Split Products",
"type": "main",
"index": 0
}
]
]
},
"Split Products": {
"main": [
[
{
"node": "Add Calculated Fields",
"type": "main",
"index": 0
}
]
]
},
"Add Calculated Fields": {
"main": [
[
{
"node": "Filter Low Stock",
"type": "main",
"index": 0
}
]
]
}
}
}
工作流说明
节点功能详解:
-
Manual Trigger - 手动触发工作流
-
Simulate API Response - 模拟从API获取的产品列表
- 返回包含products数组的JSON
-
Split Products - 拆分数据
- 输入:1个包含4个产品的item
- 输出:4个独立的item,每个代表一个产品
-
Add Calculated Fields - 使用Set节点添加新字段
in_stock: 是否有库存(布尔值)tax: 税金计算(价格×13%)final_price: 最终价格(含税)
-
Filter Low Stock - 过滤操作
- 只保留库存<100的产品
如何使用这个工作流
步骤1:导入工作流
- 在n8n中点击Import from File
- 将上述JSON保存为文件并导入
- 或直接复制JSON使用Import from URL
步骤2:运行工作流
- 点击Execute Workflow或在Manual Trigger节点点击运行按钮
- 观察每个节点的输出
步骤3:理解数据转换过程
- 在每个节点的OUTPUT面板查看数据如何变化
- 注意数据项数量的变化(1 → 4 → 可能更少)
步骤4:修改和扩展
- 修改产品数据进行测试
- 添加新的过滤条件
- 连接到真实的数据库或API
预期输出示例
执行完整工作流后,最后的Filter Low Stock节点会输出:
[
{
"json": {
"id": 1,
"name": "手机",
"price": 3999,
"stock": 50,
"category": "electronics",
"in_stock": true,
"tax": "519.87",
"final_price": "4518.87"
}
},
{
"json": {
"id": 3,
"name": "充电器",
"price": 99,
"stock": 200,
"category": "accessories",
"in_stock": true,
"tax": "12.87",
"final_price": "111.87"
}
},
{
"json": {
"id": 4,
"name": "手机壳",
"price": 49,
"stock": 500,
"category": "accessories",
"in_stock": true,
"tax": "6.37",
"final_price": "55.37"
}
}
]
注意: 平板(id: 2)被过滤掉了,因为其库存(30)小于100。
关键要点总结
✅ n8n所有数据都是JSON对象数组 - 每个item都包含一个json键
✅ 节点逐个处理item - 一个表达式应用于每个item
✅ 数据映射是数据流的基础 - 拖拽或表达式都可以实现
✅ 数据转换处理不兼容的格式 - Split Out、Aggregate等节点是你的工具
✅ 代码节点提供强大的灵活性 - Function和Code节点用于复杂操作
✅ 数据固定加速开发 - 避免重复API调用
✅ 项目链接追踪数据来源 - 高级工作流必须理解
✅ 在小规模数据存储使用数据表 - 无需外部数据库
常见问题解答
Q:为什么我的表达式 $json.fieldName 返回undefined?
A:可能是因为:
- 字段名拼写错误(JSON大小写敏感)
- 字段在嵌套对象中 - 使用
$json.parent.child - 该字段不存在 - 检查前一个节点的OUTPUT
Q:如何同时处理多个来源的数据?
A:使用Merge节点来组合多个节点的输出,然后使用表达式访问每个源的数据。
Q:Split Out和Aggregate有什么区别?
A:Split Out将1个包含列表的item分成多个item;Aggregate将多个item组合成1个。它们是相反的操作。
Q:我能在工作流中存储10GB的数据吗?
A:不能。数据表的默认限制是50MB。使用真实数据库(MySQL、PostgreSQL)来存储大规模数据。