你有没有遇到过这种情况搭n8n工作流的时候为了从一个JSON对象里抠出某个字段或者把几段文本拼在一起再发出去不得不再拖一个Code节点进来写完代码还得调试半天。其实在n8n里这类需求往往一行表达式就能搞定。今天这篇文章就专门聊聊n8n的表达式Expressions我尽量用实际能跑通的例子把语法、坑点和常见场景都讲清楚。毕竟数据处理才是工作流的核心谁也不想在流程里堆一堆笨重的代码节点。本文面向的是已经开始接触n8n、但还没系统用过表达式的朋友。如果你刚装上n8n正在为“怎么在两个节点之间传数据”“怎么把一个数组里的订单金额合计起来”这类问题头疼这篇文章会很对路。读完以后你至少能掌握表达式的基本语法、常用变量、典型数据处理姿势以及排查报错的思路。把这套东西学会了很多之前要写函数节点的活儿直接在字段里用表达式就能出结果。1. 表达式到底是个什么玩意儿1.1 带等号的字符串为什么这么重要在n8n里绝大多数输入框除了能填静态内容还支持表达式。判断方式很简单只要输入内容以开头整段内容就会被当成表达式求值输出的结果会替换掉这个占位符。比如你在HTTP Request节点的URL里填https://api.example.com/users/{{ $json.id }}这里其实混合了静态文本和表达式而单独一个字段里填{{ $json.name }}那这个字段的值就直接是当前节点数据里name属性的值。你可能会问表达式和直接写Code节点有什么区别。我的理解是表达式更像内联的计算逻辑它不需要一个完整的功能块适合做“小数据处理”而Code节点适合复杂的多步骤逻辑、循环、异步操作。表达式最大的优势是轻、快、直观而且直接在节点配置界面里就能看到输出预览不用像Code节点那样还得手动加.json返回才能看到结果。n8n的表达式基于JavaScript语法但做了很多友好的封装修饰所以你不一定要是个程序员才能用。1.2 表达式的应用场景远不止传参很多初学者以为表达式就是用来在节点之间取数的比如把上一个节点的数据传给下一个请求。这个理解没错但太狭窄了。表达式真正的威力在于在数据流动的过程中对数据进行即时加工。比如把用户输入的字符串首字母大写把一组列表中所有金额字段加起来根据当前日期动态生成“最近7天”的起止时间判断一个字段是否为空如果为空则填入默认值把一个JSON对象序列化成字符串再放到请求体里。这些操作如果都用Code节点流程会变得很碎而且数据传递链路越长排查问题越麻烦。用表达式则可以在节点字段上直接完成整个流程看起来干净多了。另外表达式有实时执行预览调试成本很低改完立刻能看到结果这一点比Code节点体验好得多。2. 从零上手表达式先认识这些核心变量2.1 $json是当前节点的数据表达式里出场率最高的变量就是$json。每个节点都有一个数据对象$json指的就是当前正在处理的那条数据的JSON内容。比如一个Webhook节点收到POST请求body里的JSON是{ name: 张三, order: { amount: 99.5, items: 2 } }那么在后面的节点里表达式字段里填{{ $json.name }}就能得到字符串“张三”填{{ $json.order.amount }}就能得到数字99.5。这就是典型的对象属性访问。如果数据里带数组比如$json.items是一个数组你就可以直接在表达式里用JavaScript的数组方法处理它。有一点要特别注意$json是“当前节点收到的单条数据”。如果上游节点传下来多条数据比如来自数据库查询的5行记录而你现在没有启用“批量执行”或逐条处理那么$json默认代表当前正在执行的那一条。想直接拿到整个多行数据的集合要看下面的$items变量。2.2 $node与$items跨节点取数与多行数据跨节点取数大概是表达式用得最频繁的场景。语法是$node[上游节点名].json加上.json是因为每个节点的输出数据都挂在json属性上。比如 {{ $node[Webhook].json.id }}就能拿到Webhook节点输出的id字段。如果上游节点名特别长写起来很累n8n在表达式编辑器里提供了可视化节点树点一下就能自动插入这个功能一定要善用。$items则用于访问一批数据。$items(节点名, 0)返回上游某个节点的所有输出数据数组$items(节点名, 0).json则是这些数据的json内容数组。配合JavaScript的map、filter、reduce方法可以做很多批量计算。比如上游节点输出了三条订单数据你想把所有订单金额合计可以写成 {{ $items(订单查询, 0).json.reduce((sum, item) sum item.amount, 0) }}需要说明的是这里$items返回的数组元素并不是纯JSON对象而是带json包装的数据结构所以表达式里要用item.amount这样的访问形式。这一点特别容易踩坑——我见过很多人直接写item.amount取不到值最后发现漏了.json这一层。2.3 其他常用变量$now是当前时间默认是ISO格式字符串能直接参与字符串拼接也能传给日期函数。$env可以读环境变量适合放API Key之类配置信息。$executionId是当前执行流的ID适合做日志追踪。$jmespath是一个内置函数支持用JMESPath语法查询复杂JSON结构不过日常用得少知道有这个东西即可。需要提一下n8n的表达式不是纯JavaScript环境而是模板字符串JS求值的混合体所以常规JavaScript语法和大部分内置对象如Math、Date、Number、String、Array都能直接用。你要是熟悉ES6语法那基本不需要额外学习成本。3. 核心实战用表达式做数据处理与变换3.1 字符串清洗从脏数据到标准格式工作中最常碰到的不是复杂的算法而是脏数据。比如从网页或者Excel导入的数据姓名里可能带着空格、大小写不统一手机号里可能夹杂横杠或括号。表达式里这些都可以半自动处理。举个例子原始字符串是 Apple Inc. 想把它转成小写并去掉首尾空格可以写 {{ $json.companyName.trim().toLowerCase() }}如果想做更复杂的替换可以用正则。比如要把手机号里的-和空格去掉 {{ $json.phone.replace(/[\s-]/g, ) }}注意替换模式末尾的g没有它只会替换第一个匹配项。这地方很容易忽略。如果你的清洗逻辑特别多比如同时要做trim、去空格、替换全角字符、统一编码我建议还是写一个Code节点统一处理因为表达式拖长了可读性会显著下降。这里再补充一个很实用的场景把两个字段拼接成一个人名和地址的完整文本。直接从不同字段取 {{ $json.name 地址 $json.address }}如果你担心字段可能为空可以稍加判断 {{ [$json.name, $json.address].filter(Boolean).join() }}filter(Boolean)的意思是剔除空字符串和null这个技巧在很多拼串场景都很省心。3.2 数组与对象加工数组处理是表达式能帮你省下大把时间的重头戏。上游节点可能会输出一个数组比如商品列表、订单列表、用户列表你需要从里面提取字段变成另一个结构或者计算汇总值。比如上游输出[ { name: 商品A, price: 30, count: 2 }, { name: 商品B, price: 25, count: 1 } ]现在想计算整单金额可以在表达式里写 {{ $json.items.reduce((sum, item) sum item.price * item.count, 0) }}想快速把价格统一打九折并生成一个新数组 {{ $json.items.map(item ({ name: item.name, price: item.price * 0.9 })) }}想按价格过滤出大于28的商品 {{ $json.items.filter(item item.price 28) }}注意这里我用了箭头函数和对象简写的现代JS语法item ({ name: ... })n8n底层是Node.js环境这些语法都支持。如果对JS语法不太熟也不必担心你只需要照着这个模式改字段名就行。对象字段的动态访问也值得一提。假设某个JSON对象的key是动态的比如有一堆以日期命名的字段{2025-06-01: 100, 2025-06-02: 200}你想根据变量取某一天的数值就可以用中括号语法 {{ $json.data[selectedDate] }}其中selectedDate是表达式里声明的一个变量。在表达式里做临时变量声明可以用IIFE写法比如 {{ (() { const d 2025-06-02; return $json.data[d]; })() }}这种写法看着复杂但在某些场景下确实绕不开。如果真的频繁需要变量那更推荐用Code节点可读性更好。3.3 动态构建请求体和输出结构在调用API时经常需要把几个字段组装成一个JSON body。以前你要么在HTTP Request节点里手写一大段JSON要么用Code节点。用表达式可以灵活拼接尤其是字段里有空值需要过滤的情况。比如要提交一个对象 {{ (() { const payload { name: $json.name, email: $json.email || 未知, age: Number($json.age) || 0 }; return JSON.stringify(payload); })() }}||在这里是“或”逻辑如果左边是空值或undefined就取右边的默认值。Number()则是把字符串“25”转成数字25防止类型不匹配。最后用JSON.stringify把对象变成JSON字符串。HTTP Request节点的body如果是JSON选项一般不需要手动stringify但在某些自定义Header或拼接raw body时这个就非常适用。反向操作JSON.parse同样常用。如果你接收到一个JSON字符串字段{a:1,b:2}想直接访问里面的a可以写 {{ JSON.parse($json.payload).a }}这种“字符串解析成对象”的用法在处理webhook传来的raw body时太常见了。4. 进阶玩法日期、条件分支与动态参数4.1 用$now处理日期和时间日期时间处理在工作流里绕不开。n8n提供了$now但它是完整的时间戳直接用来比较或拼接经常不合适。比如你想生成“昨天”这个值来查询数据库或者把当前时间格式化成YYYY-MM-DD表达式写法如下 {{ $now.toISOString().slice(0, 10) }}这样就拿到了当天的日期字符串比如2025-06-20。如果想拿昨天的时间 {{ new Date($now.getTime() - 24*60*60*1000).toISOString().slice(0, 10) }}在n8n表达式里$now是一个Date对象所以可以直接调用Date的方法。上面这种用时间戳加减天数的方式挺通用但要注意时区问题。toISOString()返回的是UTC时间。如果你的业务时区是UTC8这个方法拿到的日期可能比本地时间早一天。处理时区更稳妥的方式是利用toLocaleDateString加时区参数 {{ $now.toLocaleDateString(zh-CN, { timeZone: Asia/Shanghai }) }}这种写法格式和分隔符可能与你想的不太一样需要的话再用replace调整。做定时任务或者日报类流程时日期对错直接影响结果这块值得多测几次。4.2 条件判断让流程分支更聪明n8n的IF节点本身就是做分支的但有时候你需要的不是划分两条分支而是同一个节点内部根据条件动态改变参数。比如给不同等级的用户发送不同内容的Push通知 {{ $json.userLevel vip ? 尊贵的VIP用户感谢您的支持 : 欢迎使用我们的服务 }}这是最简单的三元表达式。如果你要基于多个条件组合比如“VIP用户且下单金额超过100元”才发特定内容可以写 {{ $json.userLevel vip $json.amount 100 ? 高级会员专享优惠 : 最新活动请查看 }}是逻辑“且”||是逻辑“或”。不少新手把这两个搞混特别是在写分支条件时。建议时刻提醒自己需要“同时满足”用“满足一个即可”用||。条件还可以作用于数字比较。比如上游节点计算出的分数$json.score需要生成等级 {{ $json.score 90 ? A : $json.score 80 ? B : $json.score 60 ? C : D }}这种多级嵌套三元在表达式里是允许的可读性还行。再复杂的话建议拆分到多个节点避免表达式变成天书。4.3 空值处理不处理空值流程迟早翻车数据处理时最烦的不是数据不对而是数据为空。Webhook漏传了字段、数据库查询没有结果、API返回了null这些都会让表达式直接抛错。我个人总结了几种常用的空值手段用||给默认值 {{ $json.nickname || 匿名用户 }}注意这里有个坑如果字段是数字00 || 匿名的结果会是“匿名”因为0被视为falsy。要保留数字0可以用??空值合并运算符 {{ $json.count ?? 0 }}??只在左侧为null或undefined时返回右侧值0不会被误判。这个细节对于处理数字字段尤其重要。如果要判断对象本身是否存在再安全地访问属性可以用可选链?. {{ $json.profile?.city ?? 未知城市 }}这样就算profile是undefined也不会报错而是返回后面的默认值。这三个运算符||、??、?.是我写表达式时的三件套强烈建议熟练掌握。5. 常见报错和排查技巧实录5.1 表达式不生效先看看等号前缀这个坑很基础但出镜率极高。你在字段里填了一段{{ $json.name }}运行后发现原样输出了这串字符又或者干脆报错提示“Expression expected”。原因往往是忘了在最前面写等号。n8n只把以开头的内容当成表达式没写就纯文本。正确的写法是{{ $json.name }}。另外n8n的表达式默认支持{{ }}占位符写法你可以在表达式的任意部分插入动态值。比如今天天气{{ $json.weather }}。这种写法类似模板字符串遇到需要拼接的场景特别好用。但如果整段内容没有开头中间的{{ }}也不会被解析。还有一个高频细节括号和引号必须成对。中文输入法会在你打代码时顺手输出中文引号、中文括号n8n环境下会直接报语法错误。写表达式时尽量保持英文半角输入。一眼望去代码是花花绿绿的颜色其实是在提示你语法有问题。5.2 数据取不到值层级路径对不对取不到值最典型的原因有三个第一个是漏了.json。跨节点取数必须写$node[节点名].json很多人写成$node[节点名]结果返回的是包含元数据的大对象不是业务数据。第二个是路径层级不对。比如你看到Webhook节点输出的结构是{ body: { user: { id: 123 } } }那么取id就应该是$node[Webhook].json.body.user.id写成.json.user.id肯定拿不到。遇到这种问题最好的办法是在n8n里点开上游节点的输出JSON仔细对照层级复制路径而不是靠猜。这个细节新手很容易忽略却能省下大把调试时间。第三个是当前节点数据不是你以为的对象。比如在IF节点的true分支后面数据结构和IF节点之前可能已经变了某些字段会被包一层。这类问题排查起来没有捷径只能一层层逐级打印或者看节点输出预览。5.3 类型不匹配字符串和数字别硬碰JavaScript是弱类型语言n8n表达式也继承了这个特性但恰恰因为弱类型很多问题要到真正运行时才暴露。比如数据库里存的年龄是字符串30你用和数字30比较结果是false。机灵一点的写法是显式转换 {{ Number($json.age) 30 }}类似的还有布尔值。HTTP请求带回的字段可能是字符串true你以为它是布尔true做条件判断时不成立。可以这样转换 {{ $json.flag true || $json.flag true }}更稳妥的是统一在入口节点做完类型转换让后续节点都用干净的数据。数组相关的报错也很多。比如你用$json.items.length想求数组长度结果items根本不是数组而是一个对象。这种情况下表达式会返回undefined看起来不报错但结果完全不对。建议先用Array.isArray()判断一下 {{ Array.isArray($json.items) ? $json.items.length : 0 }}5.4 调试表达式的几个实用姿势n8n表达式编辑器底部自带一个“试试看”的输出预览区域建议在写表达式时随时看一下。如果结果不符合预期排查顺序一般是先确认变量名和路径再确认运算符优先级最后确认类型。如果你实在查不出原因可以临时加一个Code节点把关键数据打印到日志里。Code节点里写console.log($json)然后看Execution日志。这个方法虽然粗暴但在复杂流程里效率极高。等排查完再删掉调试节点即可。另外一个技巧是尽量把表达式拆成多段一步一步验证。比如拼一个复杂字符串可以先单独验证第一个字段取数对不对再逐步加上拼接逻辑最后加上条件判断。不要一股脑写一大长串出了错很难定位。6. 两个值得养成的使用习惯6.1 常用表达式存起来复用如果你发现某个表达式反复用到比如“日期格式化”“金额两位小数”“字段默认值”可以考虑在n8n的Code节点或Function节点里封装成公共函数或者直接保存一个模板节点在个人模板库。说实话n8n官方没有太完善的表达式复用机制但你可以通过节点模板功能把常用的处理链路整段保存下来下次直接拖进去改字段名就能用。我自己就存了几套常用模板一套是“字符串清洗去重排序”一套是“订单金额计算格式化”。到了新项目里直接拖出来改成对应字段能省很多事。6.2 优先考虑表达式但不硬扛复杂逻辑核心原则是能用表达式解决的问题优先用表达式但不要为了追求“一行流”把表达式写得比Js代码还复杂。我在实际项目中见过有人把一段20行的逻辑硬塞进表达式字段里结果每次调试都在和语法较劲。那种复杂度已经完全超出表达式的舒适区用Code节点才是合理选择。一般来说表达式适合这样的场景逻辑控制在3到5行内主要是字段取值、转换、拼接、简单判断。如果你的表达式涉及多层嵌套、大量临时变量、条件分支超过3个就果断拆成Code节点或者用多个节点串联。7. 写在最后一次真实踩坑的例子有一次我搭了一个订单通知流程需要把两个来源的订单数据合并。A来源是Webhook推送B来源是数据库查询。我在合并节点里写了一个不算复杂的表达式要在满足金额大于100且用户等级为VIP时把备注字段拼上“高优先级”字样。结果测试的时候备注字段永远为空。查了半天原因是数据库查出来的userLevel字段是字符串vip 后面带了个空格导致 vip永远为false。解决办法是在表达式里先做一次trim() {{ $json.userLevel.trim() vip $json.amount 100 ? 高优先级 : }}改完立刻就好了。这个问题的根源就在脏数据而表达式里加入轻量清洗就能解决。类似这种数据源字段前后空格、大小写不一致的问题在真实业务流程里出现频率极高所以我在处理任何外部数据源时都会先假设字段可能是脏的该trim就trim该toLowerCase就toLowerCase。最后再分享一个小技巧n8n表达式就是JavaScript的子集所以你在写表达式时不妨把它当成是在写“一行JS”脑子里回顾一下这个数据类型、这个运算符的语义很多问题就能直接从根上避免。多准备几个自己常用的片段日常搭建工作流的速度绝对能快上一大截。
阅读完成 · 觉得有帮助?