Jenkins Pipeline 中的 NotSerializableException: LazyMap 报错 | 3个实用解决方案
Jenkins Pipeline中处理JSON解析报错NotSerializableException的三种解决方案:1)使用@NonCPS注解封装解析逻辑(推荐);2)将LazyMap转换为可序列化的HashMap;3)改用脚本式流水线。这些方法可解决Groovy 2.3+版本中JsonSlurper返回的LazyMap不支持序列化的问题,适用于不同场景需求。
大家好!在使用 Jenkins Pipeline 时,你是否遇到过类似以下的报错?
NotSerializableException: groovy.json.internal.LazyMap
这个看似棘手的异常,其实与 Groovy 版本的更新有关。简单来说,从 Groovy 2.3 起(Jenkins 2.7.1 所用的是 2.4.7),默认的 JsonSlurper 在解析 JSON 时会返回一种叫 LazyMap 的结构。它虽然节省内存,却不支持序列化,而 Pipeline 在执行中经常需要序列化数据,因此直接使用就容易抛出异常。

常见问题代码如下:
def jsonResponse = sh(script: 'curl -s http://example.com/api/data', returnStdout: true).trim()
def jsonSlurper = new groovy.json.JsonSlurper()
def result = jsonSlurper.parseText(jsonResponse) // 这里得到的是 LazyMap
println(result) // 触发 NotSerializableException!
别担心,下面为大家分享三个实用的解决方案,帮你轻松绕过此坑。
| 方案 | 适用场景 | 备注 |
|---|---|---|
| @NonCPS 注解 | 推荐首选,逻辑独立、不需与 Pipeline 步骤交互时 | 最清晰、最规范 |
| 转换为 HashMap | 只需简单转换,数据结构较平坦时 | 注意嵌套结构的拷贝问题 |
| 改用脚本式流水线 | 已在脚本式环境中,或声明式限制过多时 | 灵活,但需注意代码风格统一 |
简单来说,遇到 LazyMap 导致的序列化错误时,优先考虑使用 @NonCPS 注解封装解析逻辑,这通常是最干净、最可持续的做法。
✅ 解决方案一:使用 @NonCPS 注解(推荐)
这是 最直接、最推荐 的做法。
通过在方法上添加 @NonCPS 注解,可以告诉 Jenkins 这个方法内的操作不需要参与序列化,从而允许使用 LazyMap 等非序列化对象。
示例:
@NonCPS
def parseJson(String jsonText) {
return new groovy.json.JsonSlurper().parseText(jsonText)
}
node {
def jsonResponse = sh(script: 'curl -s http://example.com/api/data', returnStdout: true).trim()
def result = parseJson(jsonResponse) // 安全,不会触发异常
println(result)
}
小贴士:@NonCPS 方法内应尽量保持纯 Groovy 逻辑,避免调用 Jenkins Pipeline 的步骤(如 sh, echo 等)。
✅ 解决方案二:将 LazyMap 转换为可序列化的 Map
如果不想用 @NonCPS,也可以手动将 LazyMap 转换为标准的、可序列化的 HashMap。
示例:
def jsonResponse = sh(script: 'curl -s http://example.com/api/data', returnStdout: true).trim()
def jsonSlurper = new groovy.json.JsonSlurper()
def lazyMap = jsonSlurper.parseText(jsonResponse)
def serializableMap = new HashMap<>(lazyMap) // 关键转换
println(serializableMap) // 现在可以安全使用了
注意: 这种方式对于嵌套较深的 JSON 结构是浅拷贝,如果内层仍有 LazyMap,可能仍需递归转换。
✅ 解决方案三:改用脚本式流水线(Scripted Pipeline)
如果你在使用声明式流水线(Declarative Pipeline),可以尝试在特定阶段切回脚本式流水线写法。脚本式流水线在序列化控制上更为灵活,有时能自然规避此类问题。
示例:
node {
stage('Process JSON') {
def jsonResponse = sh(script: 'curl -s http://example.com/api/data', returnStdout: true).trim()
def jsonSlurper = new groovy.json.JsonSlurper()
def result = jsonSlurper.parseText(jsonResponse)
println(result.key) // 在脚本式块内通常可直接使用
}
}
-------------------------------------- 🚀 Powered by Moshow 郑锴- 🌟 Might the holy code be with you!-------------------------------------🔍 公众号 👉 软件开发大百科💻 CSDN 👉 https://zhengkai.blog.csdn.net📂 GitHub 👉 https://github.com/moshowgame
更多推荐
所有评论(0)