在使用n8n进行工作流自动化时,数据处理是核心技能。无论是从API获取数据、转换格式、还是在不同节点之间映射数据,理解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张新卡片。
这种设计让你可以:
数据映射是指从前一个节点的输出中引用数据,并将其传递给下一个节点的过程。它不涉及数据转换,只是引用。
这是初学者最推荐的方法:
步骤:
例子:
假设你的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 }}对于更复杂的需求,你可以直接在表达式编辑器中编写:
// 访问顶级字段
{{ $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[1] |
| 订单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数据透视表的聚合 | 生成统计数据 |
场景: 你的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节点 | Code节点 |
|---|---|---|
| 语言 | JavaScript | JavaScript / Python |
| 复杂度 | 简单单行代码 | 更复杂的逻辑 |
| 自动处理 | 自动添加json键和数组 | 从0.166.0版本后也自动处理 |
| 文件大小 | 不可处理 | 可处理 |
场景: 你需要为每个用户生成一个欢迎邮件,并创建一个新字段 greeting
// 输入:{ "json": { "name": "李四" } }
return {
json: {
...($json), // 保留原有字段
greeting: `尊敬的 ${$json.name},欢迎使用我们的服务!`,
processed_at: new Date().toISOString()
}
};
// 输出:
// {
// "json": {
// "name": "李四",
// "greeting": "尊敬的 李四,欢迎使用我们的服务!",
// "processed_at": "2025-12-04T14:02:00Z"
// }
// }// 处理所有输入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键和数组包装,让代码编写更简洁。
数据固定是指保存节点的输出数据,并在后续测试中使用这个保存的数据,而不是每次都重新获取。
好处:
现在,即使外部系统数据改变,下次运行时也会使用固定的数据。
有时你想测试不同的场景。你可以编辑固定的数据:
这对于测试边界情况非常有用:
当节点处理多个item时,n8n会自动跟踪每个输出item与哪个输入item相关。这就是项目链接。
n8n会自动尝试链接item:
| 输入 | 输出 | 链接方式 |
|---|---|---|
| 1个 | 1个 | 输出链接到输入 |
| 1个 | 多个 | 所有输出都链接到这个输入 |
| 多个 | 多个(数量相等) | 按顺序一一对应 |
在表达式编辑器中,你可以访问之前节点中的item:
// 访问前一个节点中的链接item
{{ $item(0).$json.original_field }}
// 在多步工作流中回溯
{{ $item("NodeName").$json.fieldName }}例子:
假设你有一个工作流:
在Google Sheets节点中,如果你想引用HTTP Request节点中的原始用户名:
{{ $item("HTTP Request").$json.username }}数据表是n8n内置的轻量级数据存储,无需外部数据库就可以在工作流中存储和管理数据。
第一步:创建数据表
第二步:在工作流中使用
使用Data table节点来:
例子:防止重复运行
// 在Data table中存储已处理的订单ID
// 每次运行时查询这个表,检查订单是否已处理你需要创建一个工作流,用来:
以下是一个完整的可导入工作流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
}
]
]
}
}
}节点功能详解:
in_stock: 是否有库存(布尔值)tax: 税金计算(价格×13%)final_price: 最终价格(含税)步骤1:导入工作流
步骤2:运行工作流
步骤3:理解数据转换过程
步骤4:修改和扩展
执行完整工作流后,最后的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.parent.childQ:如何同时处理多个来源的数据?
A:使用Merge节点来组合多个节点的输出,然后使用表达式访问每个源的数据。
Q:Split Out和Aggregate有什么区别?
A:Split Out将1个包含列表的item分成多个item;Aggregate将多个item组合成1个。它们是相反的操作。
Q:我能在工作流中存储10GB的数据吗?
A:不能。数据表的默认限制是50MB。使用真实数据库(MySQL、PostgreSQL)来存储大规模数据。
[1] lisi@example.com: mailto:lisi@example.com
[2] 官方文档: https://docs.n8n.io/data/
[3] n8n系列教程: https://www.undsky.com/blog/?category=n8n%E6%95%99%E7%A8%8B#